<commit_before>package de.peeeq.wurstscript.validation;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.jdt.annotation.Nullable;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

import de.peeeq.wurstscript.WLogger;
import de.peeeq.wurstscript.ast.*;
import de.peeeq.wurstscript.attributes.CheckHelper;
import de.peeeq.wurstscript.attributes.CofigOverridePackages;
import de.peeeq.wurstscript.attributes.CompileError;
import de.peeeq.wurstscript.attributes.ImplicitFuncs;
import de.peeeq.wurstscript.attributes.names.NameLink;
import de.peeeq.wurstscript.attributes.names.NameLinkType;
import de.peeeq.wurstscript.attributes.names.Visibility;
import de.peeeq.wurstscript.gui.ProgressHelper;
import de.peeeq.wurstscript.jassIm.ImStmts;
import de.peeeq.wurstscript.types.CallSignature;
import de.peeeq.wurstscript.types.FunctionSignature;
import de.peeeq.wurstscript.types.WurstType;
import de.peeeq.wurstscript.types.WurstTypeArray;
import de.peeeq.wurstscript.types.WurstTypeBool;
import de.peeeq.wurstscript.types.WurstTypeClass;
import de.peeeq.wurstscript.types.WurstTypeClosure;
import de.peeeq.wurstscript.types.WurstTypeCode;
import de.peeeq.wurstscript.types.WurstTypeEnum;
import de.peeeq.wurstscript.types.WurstTypeInt;
import de.peeeq.wurstscript.types.WurstTypeInterface;
import de.peeeq.wurstscript.types.WurstTypeModule;
import de.peeeq.wurstscript.types.WurstTypeNamedScope;
import de.peeeq.wurstscript.types.WurstTypeReal;
import de.peeeq.wurstscript.types.WurstTypeString;
import de.peeeq.wurstscript.types.WurstTypeTuple;
import de.peeeq.wurstscript.types.WurstTypeUnknown;
import de.peeeq.wurstscript.types.WurstTypeVoid;
import de.peeeq.wurstscript.utils.Utils;
import de.peeeq.wurstscript.validation.controlflow.DataflowAnomalyAnalysis;
import de.peeeq.wurstscript.validation.controlflow.ReturnsAnalysis;

/**
 * this class validates a wurstscript program
 * 
 * it has visit methods for different elements in the AST and checks whether
 * these are correct
 * 
 * the validation phase might not find all errors, code transformation and
 * optimization phases might detect other errors because they do a more
 * sophisticated analysis of the program
 * 
 * also note that many cases are already caught by the calculation of the
 * attributes
 * 
 */
public class WurstValidator {

	private WurstModel prog;
	private int functionCount;
	private int visitedFunctions;
	private Multimap<WScope	, WScope> calledFunctions = HashMultimap.create();
	private @Nullable AstElement lastElement = null;

	public WurstValidator(WurstModel root) {
		this.prog = root;
	}

	public void validate(List<CompilationUnit> toCheck) {
		try {
			functionCount = countFunctions(); 
			visitedFunctions = 0;
	
			prog.getErrorHandler().setProgress("Checking wurst types", ProgressHelper.getValidatorPercent(visitedFunctions, functionCount));
			for (CompilationUnit cu : toCheck) {
				walkTree(cu);	
			}
			prog.getErrorHandler().setProgress("Post checks", 0.55);
			postChecks(toCheck);
		} catch (RuntimeException e) {
			WLogger.severe(e);
			AstElement le = lastElement;
			if (le != null) {
				le.addError("Encountered compiler bug near element " + Utils.printElement(le) + ":\n" +
						Utils.printException(e));
			} else {
				// rethrow
				throw e;
			}
		}
	}

	/**
	 * checks done after walking the tree
	 * @param toCheck 
	 */
	private void postChecks(List<CompilationUnit> toCheck) {
		checkUnusedImports(toCheck);
	}

	

	private void checkUnusedImports(List<CompilationUnit> toCheck) {
		for (CompilationUnit cu : toCheck) {
			for (WPackage p : cu.getPackages()) {
				checkUnusedImports(p);
			}
		}
	}

	private void checkUnusedImports(WPackage p) {
		Set<WPackage> unused = Sets.newLinkedHashSet();
		// first assume all are unused
		for (WImport imp : p.getImports()) {
			if (!imp.getPackagename().equals("Wurst")) {
				unused.add(imp.attrImportedPackage());
			}
		}
		
		removeUsed(unused, p.getElements());
		
		
		for (WImport imp : p.getImports()) {
			if (imp.attrImportedPackage() != null 
				&& !imp.getIsPublic()
				&& unused.contains(imp.attrImportedPackage()) ) {
				imp.addWarning("The import " + imp.getPackagename() + " is never used directly.");
			}
		}
	}

	private void removeUsed(Set<WPackage> unused, AstElement e) {
		for (int i=0; i<e.size(); i++) {
			removeUsed(unused, e.get(i));
		}
		
		if (e instanceof FuncRef) {
			FuncRef fr = (FuncRef) e;
			FunctionDefinition def = fr.attrFuncDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof NameRef) {
			NameRef nr = (NameRef) e;
			NameDef def = nr.attrNameDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof TypeRef) {
			TypeRef t = (TypeRef) e;
			TypeDef def = t.attrTypeDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof Expr) {
			WurstType typ = ((Expr) e).attrTyp();
			if (typ instanceof WurstTypeNamedScope) {
				WurstTypeNamedScope ns = (WurstTypeNamedScope) typ;
				NamedScope def = ns.getDef();
				if (def != null) {
					unused.remove(def.attrNearestPackage());
				}
			} else if (typ instanceof WurstTypeTuple) {
				TupleDef def = ((WurstTypeTuple) typ).getTupleDef();<commit_msg>unused imports warning now considers operator overloading<commit_after>package de.peeeq.wurstscript.validation;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.jdt.annotation.Nullable;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

import de.peeeq.wurstscript.WLogger;
import de.peeeq.wurstscript.ast.*;
import de.peeeq.wurstscript.attributes.CheckHelper;
import de.peeeq.wurstscript.attributes.CofigOverridePackages;
import de.peeeq.wurstscript.attributes.CompileError;
import de.peeeq.wurstscript.attributes.ImplicitFuncs;
import de.peeeq.wurstscript.attributes.names.NameLink;
import de.peeeq.wurstscript.attributes.names.NameLinkType;
import de.peeeq.wurstscript.attributes.names.Visibility;
import de.peeeq.wurstscript.gui.ProgressHelper;
import de.peeeq.wurstscript.jassIm.ImStmts;
import de.peeeq.wurstscript.types.CallSignature;
import de.peeeq.wurstscript.types.FunctionSignature;
import de.peeeq.wurstscript.types.WurstType;
import de.peeeq.wurstscript.types.WurstTypeArray;
import de.peeeq.wurstscript.types.WurstTypeBool;
import de.peeeq.wurstscript.types.WurstTypeClass;
import de.peeeq.wurstscript.types.WurstTypeClosure;
import de.peeeq.wurstscript.types.WurstTypeCode;
import de.peeeq.wurstscript.types.WurstTypeEnum;
import de.peeeq.wurstscript.types.WurstTypeInt;
import de.peeeq.wurstscript.types.WurstTypeInterface;
import de.peeeq.wurstscript.types.WurstTypeModule;
import de.peeeq.wurstscript.types.WurstTypeNamedScope;
import de.peeeq.wurstscript.types.WurstTypeReal;
import de.peeeq.wurstscript.types.WurstTypeString;
import de.peeeq.wurstscript.types.WurstTypeTuple;
import de.peeeq.wurstscript.types.WurstTypeUnknown;
import de.peeeq.wurstscript.types.WurstTypeVoid;
import de.peeeq.wurstscript.utils.Utils;
import de.peeeq.wurstscript.validation.controlflow.DataflowAnomalyAnalysis;
import de.peeeq.wurstscript.validation.controlflow.ReturnsAnalysis;

/**
 * this class validates a wurstscript program
 * 
 * it has visit methods for different elements in the AST and checks whether
 * these are correct
 * 
 * the validation phase might not find all errors, code transformation and
 * optimization phases might detect other errors because they do a more
 * sophisticated analysis of the program
 * 
 * also note that many cases are already caught by the calculation of the
 * attributes
 * 
 */
public class WurstValidator {

	private WurstModel prog;
	private int functionCount;
	private int visitedFunctions;
	private Multimap<WScope	, WScope> calledFunctions = HashMultimap.create();
	private @Nullable AstElement lastElement = null;

	public WurstValidator(WurstModel root) {
		this.prog = root;
	}

	public void validate(List<CompilationUnit> toCheck) {
		try {
			functionCount = countFunctions(); 
			visitedFunctions = 0;
	
			prog.getErrorHandler().setProgress("Checking wurst types", ProgressHelper.getValidatorPercent(visitedFunctions, functionCount));
			for (CompilationUnit cu : toCheck) {
				walkTree(cu);	
			}
			prog.getErrorHandler().setProgress("Post checks", 0.55);
			postChecks(toCheck);
		} catch (RuntimeException e) {
			WLogger.severe(e);
			AstElement le = lastElement;
			if (le != null) {
				le.addError("Encountered compiler bug near element " + Utils.printElement(le) + ":\n" +
						Utils.printException(e));
			} else {
				// rethrow
				throw e;
			}
		}
	}

	/**
	 * checks done after walking the tree
	 * @param toCheck 
	 */
	private void postChecks(List<CompilationUnit> toCheck) {
		checkUnusedImports(toCheck);
	}

	

	private void checkUnusedImports(List<CompilationUnit> toCheck) {
		for (CompilationUnit cu : toCheck) {
			for (WPackage p : cu.getPackages()) {
				checkUnusedImports(p);
			}
		}
	}

	private void checkUnusedImports(WPackage p) {
		Set<WPackage> unused = Sets.newLinkedHashSet();
		// first assume all are unused
		for (WImport imp : p.getImports()) {
			if (!imp.getPackagename().equals("Wurst")) {
				unused.add(imp.attrImportedPackage());
			}
		}
		
		removeUsed(unused, p.getElements());
		
		
		for (WImport imp : p.getImports()) {
			if (imp.attrImportedPackage() != null 
				&& !imp.getIsPublic()
				&& unused.contains(imp.attrImportedPackage()) ) {
				imp.addWarning("The import " + imp.getPackagename() + " is never used directly.");
			}
		}
	}

	private void removeUsed(Set<WPackage> unused, AstElement e) {
		for (int i=0; i<e.size(); i++) {
			removeUsed(unused, e.get(i));
		}
		
		if (e instanceof FuncRef) {
			FuncRef fr = (FuncRef) e;
			FunctionDefinition def = fr.attrFuncDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof NameRef) {
			NameRef nr = (NameRef) e;
			NameDef def = nr.attrNameDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof TypeRef) {
			TypeRef t = (TypeRef) e;
			TypeDef def = t.attrTypeDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof ExprBinary) {
			ExprBinary binop = (ExprBinary) e;
			FunctionDefinition def = binop.attrFuncDef();
			if (def != null) {
				unused.remove(def.attrNearestPackage());
			}
		}
		if (e instanceof Expr) {
			WurstType typ = ((Expr) e).attrTyp();
			if (typ instanceof WurstTypeNamedScope) {
				WurstTypeNamedScope ns = (WurstTypeNamedScope) typ;
				NamedScope def = ns.getDef();
				if (def != null) {
					unused.remove(def.attrNearestPackage());
				}
			} else if (typ instanceof WurstTypeTuple) {
				TupleDef def = ((WurstTypeTuple) typ).getTupleDef();
{'fraction_non_alphanumeric': 0.1147136563876652, 'fraction_numerical': 0.000881057268722467, 'mean_word_length': 4.436302681992337, 'pattern_counts': {'":': 2, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2407261', 'n_tokens_mistral': 4138, 'n_tokens_neox': 3872, 'n_words': 780}