<commit_before>/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.detect;

import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Synthetic;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.ClassSummary;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.Hierarchy2;
import edu.umd.cs.findbugs.ba.IncompatibleTypes;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
import edu.umd.cs.findbugs.ba.SignatureParser;
import edu.umd.cs.findbugs.ba.TestCaseDetector;
import edu.umd.cs.findbugs.ba.XClass;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.generic.GenericObjectType;
import edu.umd.cs.findbugs.ba.generic.GenericUtilities;
import edu.umd.cs.findbugs.ba.generic.GenericUtilities.TypeCategory;
import edu.umd.cs.findbugs.ba.type.TopType;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.type.TypeFrame;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;

/**
 * @author <NAME>
 */
public class FindUnrelatedTypesInGenericContainer implements Detector {

	private BugReporter bugReporter;

	private static final boolean DEBUG = SystemProperties.getBoolean("gc.debug");

	/** 
	 * Map classname, methodname and signature to an int []. 
	 * Each position in the int [] corresponds to an argument in the methodSignature.
	 * For each argument i, the value at position i corresponds to the index of the 
	 * corresponding type in the class type parameters. If the argument
	 * has no correspondence, then the value is -1. <p>
	 * 
	 * Get the String key by calling getCollectionsMapKey()
	 */
	
	
	private Map<String, ClassDescriptor> nameToInterfaceMap = new HashMap<String,ClassDescriptor>();
	private Map<String, Integer> nameToTypeArgumentIndex = new HashMap<String,Integer>();
	

	private void addToCollectionsMap(@DottedClassName String className, String methodName, 
			int argumentParameterIndex) {
		ClassDescriptor c = DescriptorFactory.instance().getClassDescriptorForDottedClassName(className);
		nameToInterfaceMap.put(methodName, c);
		nameToTypeArgumentIndex.put(methodName, argumentParameterIndex);
		}

	
	public FindUnrelatedTypesInGenericContainer(BugReporter bugReporter) {
		this.bugReporter = bugReporter;
		String basicSignature = "(Ljava/lang/Object;)Z";
		String collectionSignature = "(Ljava/util/Collection<*>;)Z";
		String indexSignature = "(Ljava/lang/Object;)I";

			// Collection<E>
			addToCollectionsMap(Collection.class.getName(), "contains", 0);
			addToCollectionsMap(Collection.class.getName(), "remove",   0);

			//addToCollectionsMap(collectionMembers, "containsAll", collectionSignature, 0);
			//addToCollectionsMap(collectionMembers, "removeAll",   collectionSignature, 0);
			//addToCollectionsMap(collectionMembers, "retainAll",   collectionSignature, 0);

			// List<E>
			addToCollectionsMap(List.class.getName(), "indexOf", 0);
			addToCollectionsMap(List.class.getName(), "lastIndexOf", 0);

			// Map<K,V>
			addToCollectionsMap(Map.class.getName(), "containsKey", 0);
			addToCollectionsMap(Map.class.getName(), "containsValue", 1);

			// Map<K,V>
			addToCollectionsMap(Map.class.getName(), "get", 0);
			addToCollectionsMap(Map.class.getName(), "remove", 0);

			}

	/**
	 * Visit the class context
	 * @see edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba.ClassContext)
	 */
	public void visitClassContext(ClassContext classContext) {
		JavaClass javaClass = classContext.getJavaClass();
		Method[] methodList = javaClass.getMethods();

		for (Method method : methodList) {
			if (method.getCode() == null)
				continue;

			try {
				analyzeMethod(classContext, method);
			} catch (MethodUnprofitableException e) {
				assert true; // move along; nothing to see
			} catch (CFGBuilderException e) {
				String msg = "Detector " + this.getClass().getName()
										+ " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
				bugReporter.logError(msg , e);
			} catch (DataflowAnalysisException e) {
				String msg = "Detector " + this.getClass().getName()
										+ " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
				bugReporter.logError(msg, e);
			}
		}
	}

	/**
	 * Use this to screen out methods that do not contain invocations.
	 */
	public boolean prescreen(ClassContext classContext, Method method) {
		BitSet bytecodeSet = classContext.getBytecodeSet(method);
		return bytecodeSet != null && (
				  bytecodeSet.get(Constants.INVOKEINTERFACE) || 
				  bytecodeSet.get(Constants.INVOKEVIRTUAL)   ||
				  bytecodeSet.get(Constants.INVOKESPECIAL) 	 ||
				  bytecodeSet.get(Constants.INVOKESTATIC)    ||
				  bytecodeSet.get(Constants.INVOKENONVIRTUAL)				  
				);
	}

	/**
	 * Methods marked with the "Synthetic" attribute do not appear 
	 * in the source code
	 */
	private boolean isSynthetic(Method m) {
		Attribute[] attrs = m.getAttributes();
		for (Attribute attr : attrs) {
			if (attr instanceof Synthetic)
				return true;
		}
		return false;
	}

	private void analyzeMethod(ClassContext classContext, Method method)
	throws CFGBuilderException, DataflowAnalysisException {
		if (isSynthetic(method) || !prescreen(classContext, method))
			return;

		BugAccumulator accumulator = new BugAccumulator(bugReporter);

		CFG cfg = classContext.getCFG(method);
		TypeDataflow typeDataflow = classContext.getTypeDataflow(method);

		ConstantPoolGen cpg = classContext.getConstantPoolGen();
		MethodGen methodGen = classContext.getMethodGen(method);
		if (methodGen == null) return;
		String fullMethodName = 
			methodGen.getClassName() + "." + methodGen.getName(); 

		String sourceFile = classContext.getJavaClass().getSourceFileName();
		if (DEBUG) {
			System.out.println("Checking " + fullMethodName);
		}

		// Process each instruction
		for (Iterator<Location> iter = cfg.locationIterator(); iter.hasNext();) {
			Location location = iter.next();
			InstructionHandle handle = location.getHandle();
			Instruction ins = handle.getInstruction();

			// Only consider invoke instructions
			if (!(ins instanceof InvokeInstruction))
				continue;

			InvokeInstruction inv = (InvokeInstruction)ins;

			XMethod m = XFactory.createXMethod(inv, cpg);
			
			ClassDescriptor interfaceOfInterest = nameToInterfaceMap.get(m.getName());
			if (interfaceOfInterest == null) continue;
			String argSignature = m.getSignature();
			argSignature = argSignature.substring(0,argSignature.indexOf(')')+1);
			if (!argSignature.equals("(Ljava/lang/Object;)")) continue;
			
			Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
			try {
	            if (!subtypes2.isSubtype(m.getClassDescriptor(), interfaceOfInterest)) continue;
	        } catch (ClassNotFoundException e) {
	           AnalysisContext.reportMissingClass(e);
	           continue;
            }
			// OK, we've fold a method call of interest
           
            
			
			int typeArgument = nameToTypeArgumentIndex.get(m.getName());
			
			TypeFrame frame = typeDataflow.getFactAtLocation(location);
			if (!frame.isValid()) {
				// This basic block is probably dead
				continue;
			}

			Type operandType = frame.getTopValue();
			if (operandType.equals(TopType.instance())) {
				// unreachable
				continue;
			}			

			// Only consider generic... 
			Type objectType = frame.getInstance(inv, cpg);			
			if (!(objectType instanceof GenericObjectType))
				continue;

			GenericObjectType operand = (GenericObjectType) objectType;

			// ... containers
			if (!operand.hasParameters()) continue;

			int expectedParameters = 1;
			if (interfaceOfInterest.getSimpleName().equals("Map")) expectedParameters = 2;
			if (operand.getNumParameters() != expectedParameters) continue;
			
			int numArguments = frame.getNumArguments(inv, cpg);

			if (numArguments != 1)
				continue; 

			// compare containers type parameters to corresponding arguments
			SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg));

			Type parmType = operand.getParameterAt(typeArgument);
			Type argType = frame.getArgument(inv, cpg, 0, sigParser);
			IncompatibleTypes  matchResult = compareTypes(parmType, argType);

			if (matchResult == IncompatibleTypes.SEEMS_OK) continue;

			// Prepare bug report
			SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
			.fromVisitedInstruction(classContext, methodGen, sourceFile, handle);

			// Report a bug that mentions each of the failed arguments in matches

			if (parmType instanceof GenericObjectType)
				parmType = ((GenericObjectType)parmType).getUpperBound();

			int priority = matchResult.getPriority();
			XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
			if (TestCaseDetector.likelyTestCase(xmethod))
				priority = Priorities.LOW_PRIORITY;
			ClassDescriptor expectedClassDescriptor = DescriptorFactory.createClassDescriptorFromSignature(parmType.getSignature());
			if (AnalysisContext.currentAnalysisContext().getClassSummary().mightBeEqualToOtherClasses(expectedClassDescriptor))
				priority++;
			accumulator.accumulateBug(new BugInstance(this,
					"GC_UNRELATED_TYPES", priority)
			.addClassAndMethod(methodGen, sourceFile)					
			.addFoundAndExpectedType(argType.getSignature(), parmType.getSignature())
			.addEqualsMethodUsed(expectedClassDescriptor)
			.addCalledMethod(methodGen, (InvokeInstruction) ins)
			,sourceLineAnnotation);
		}
		accumulator.reportAccumulatedBugs();
	}

	/**
	 * Compare to see if the argument <code>argType</code> passed to the method 
	 * matches the type of the corresponding parameter. The simplest case is when
	 * both are equal. <p>
	 * This is a conservative comparison - returns true if it cannot decide.
	 * If the parameter type is a type variable (e.g. <code>T</code>) then we don't 
	 * know enough (yet) to decide if they do not match so return true.
	 */
	private IncompatibleTypes compareTypes(Type parmType, Type argType) {
		// XXX equality not implemented for GenericObjectType
		// if (parmType.equals(argType)) return true;
		// Compare type signatures instead
		String parmString = GenericUtilities.getString(parmType);<commit_msg>containers use equals method of key<commit_after>/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.detect;

import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.Synthetic;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.ClassSummary;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.Hierarchy2;
import edu.umd.cs.findbugs.ba.IncompatibleTypes;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
import edu.umd.cs.findbugs.ba.SignatureParser;
import edu.umd.cs.findbugs.ba.TestCaseDetector;
import edu.umd.cs.findbugs.ba.XClass;
import edu.umd.cs.findbugs.ba.XFactory;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.ba.ch.Subtypes2;
import edu.umd.cs.findbugs.ba.generic.GenericObjectType;
import edu.umd.cs.findbugs.ba.generic.GenericUtilities;
import edu.umd.cs.findbugs.ba.generic.GenericUtilities.TypeCategory;
import edu.umd.cs.findbugs.ba.type.TopType;
import edu.umd.cs.findbugs.ba.type.TypeDataflow;
import edu.umd.cs.findbugs.ba.type.TypeFrame;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;

/**
 * @author <NAME>
 */
public class FindUnrelatedTypesInGenericContainer implements Detector {

	private BugReporter bugReporter;

	private static final boolean DEBUG = SystemProperties.getBoolean("gc.debug");

	/** 
	 * Map classname, methodname and signature to an int []. 
	 * Each position in the int [] corresponds to an argument in the methodSignature.
	 * For each argument i, the value at position i corresponds to the index of the 
	 * corresponding type in the class type parameters. If the argument
	 * has no correspondence, then the value is -1. <p>
	 * 
	 * Get the String key by calling getCollectionsMapKey()
	 */
	
	
	private Map<String, ClassDescriptor> nameToInterfaceMap = new HashMap<String,ClassDescriptor>();
	private Map<String, Integer> nameToTypeArgumentIndex = new HashMap<String,Integer>();
	

	private void addToCollectionsMap(@DottedClassName String className, String methodName, 
			int argumentParameterIndex) {
		ClassDescriptor c = DescriptorFactory.instance().getClassDescriptorForDottedClassName(className);
		nameToInterfaceMap.put(methodName, c);
		nameToTypeArgumentIndex.put(methodName, argumentParameterIndex);
		}

	
	public FindUnrelatedTypesInGenericContainer(BugReporter bugReporter) {
		this.bugReporter = bugReporter;
		String basicSignature = "(Ljava/lang/Object;)Z";
		String collectionSignature = "(Ljava/util/Collection<*>;)Z";
		String indexSignature = "(Ljava/lang/Object;)I";

			// Collection<E>
			addToCollectionsMap(Collection.class.getName(), "contains", 0);
			addToCollectionsMap(Collection.class.getName(), "remove",   0);

			//addToCollectionsMap(collectionMembers, "containsAll", collectionSignature, 0);
			//addToCollectionsMap(collectionMembers, "removeAll",   collectionSignature, 0);
			//addToCollectionsMap(collectionMembers, "retainAll",   collectionSignature, 0);

			// List<E>
			addToCollectionsMap(List.class.getName(), "indexOf", 0);
			addToCollectionsMap(List.class.getName(), "lastIndexOf", 0);

			// Map<K,V>
			addToCollectionsMap(Map.class.getName(), "containsKey", 0);
			addToCollectionsMap(Map.class.getName(), "containsValue", 1);

			// Map<K,V>
			addToCollectionsMap(Map.class.getName(), "get", 0);
			addToCollectionsMap(Map.class.getName(), "remove", 0);

			}

	/**
	 * Visit the class context
	 * @see edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba.ClassContext)
	 */
	public void visitClassContext(ClassContext classContext) {
		JavaClass javaClass = classContext.getJavaClass();
		Method[] methodList = javaClass.getMethods();

		for (Method method : methodList) {
			if (method.getCode() == null)
				continue;

			try {
				analyzeMethod(classContext, method);
			} catch (MethodUnprofitableException e) {
				assert true; // move along; nothing to see
			} catch (CFGBuilderException e) {
				String msg = "Detector " + this.getClass().getName()
										+ " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
				bugReporter.logError(msg , e);
			} catch (DataflowAnalysisException e) {
				String msg = "Detector " + this.getClass().getName()
										+ " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
				bugReporter.logError(msg, e);
			}
		}
	}

	/**
	 * Use this to screen out methods that do not contain invocations.
	 */
	public boolean prescreen(ClassContext classContext, Method method) {
		BitSet bytecodeSet = classContext.getBytecodeSet(method);
		return bytecodeSet != null && (
				  bytecodeSet.get(Constants.INVOKEINTERFACE) || 
				  bytecodeSet.get(Constants.INVOKEVIRTUAL)   ||
				  bytecodeSet.get(Constants.INVOKESPECIAL) 	 ||
				  bytecodeSet.get(Constants.INVOKESTATIC)    ||
				  bytecodeSet.get(Constants.INVOKENONVIRTUAL)				  
				);
	}

	/**
	 * Methods marked with the "Synthetic" attribute do not appear 
	 * in the source code
	 */
	private boolean isSynthetic(Method m) {
		Attribute[] attrs = m.getAttributes();
		for (Attribute attr : attrs) {
			if (attr instanceof Synthetic)
				return true;
		}
		return false;
	}

	private void analyzeMethod(ClassContext classContext, Method method)
	throws CFGBuilderException, DataflowAnalysisException {
		if (isSynthetic(method) || !prescreen(classContext, method))
			return;

		BugAccumulator accumulator = new BugAccumulator(bugReporter);

		CFG cfg = classContext.getCFG(method);
		TypeDataflow typeDataflow = classContext.getTypeDataflow(method);

		ConstantPoolGen cpg = classContext.getConstantPoolGen();
		MethodGen methodGen = classContext.getMethodGen(method);
		if (methodGen == null) return;
		String fullMethodName = 
			methodGen.getClassName() + "." + methodGen.getName(); 

		String sourceFile = classContext.getJavaClass().getSourceFileName();
		if (DEBUG) {
			System.out.println("Checking " + fullMethodName);
		}

		// Process each instruction
		for (Iterator<Location> iter = cfg.locationIterator(); iter.hasNext();) {
			Location location = iter.next();
			InstructionHandle handle = location.getHandle();
			Instruction ins = handle.getInstruction();

			// Only consider invoke instructions
			if (!(ins instanceof InvokeInstruction))
				continue;

			InvokeInstruction inv = (InvokeInstruction)ins;

			XMethod m = XFactory.createXMethod(inv, cpg);
			
			ClassDescriptor interfaceOfInterest = nameToInterfaceMap.get(m.getName());
			if (interfaceOfInterest == null) continue;
			String argSignature = m.getSignature();
			argSignature = argSignature.substring(0,argSignature.indexOf(')')+1);
			if (!argSignature.equals("(Ljava/lang/Object;)")) continue;
			
			Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
			try {
	            if (!subtypes2.isSubtype(m.getClassDescriptor(), interfaceOfInterest)) continue;
	        } catch (ClassNotFoundException e) {
	           AnalysisContext.reportMissingClass(e);
	           continue;
            }
			// OK, we've fold a method call of interest
           
            
			
			int typeArgument = nameToTypeArgumentIndex.get(m.getName());
			
			TypeFrame frame = typeDataflow.getFactAtLocation(location);
			if (!frame.isValid()) {
				// This basic block is probably dead
				continue;
			}

			Type operandType = frame.getTopValue();
			if (operandType.equals(TopType.instance())) {
				// unreachable
				continue;
			}			

			// Only consider generic... 
			Type objectType = frame.getInstance(inv, cpg);			
			if (!(objectType instanceof GenericObjectType))
				continue;

			GenericObjectType operand = (GenericObjectType) objectType;

			// ... containers
			if (!operand.hasParameters()) continue;

			int expectedParameters = 1;
			if (interfaceOfInterest.getSimpleName().equals("Map")) expectedParameters = 2;
			if (operand.getNumParameters() != expectedParameters) continue;
			
			int numArguments = frame.getNumArguments(inv, cpg);

			if (numArguments != 1)
				continue; 

			// compare containers type parameters to corresponding arguments
			SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg));

			Type parmType = operand.getParameterAt(typeArgument);
			Type argType = frame.getArgument(inv, cpg, 0, sigParser);
			IncompatibleTypes  matchResult = compareTypes(parmType, argType);

			if (matchResult == IncompatibleTypes.SEEMS_OK) continue;

			// Prepare bug report
			SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
			.fromVisitedInstruction(classContext, methodGen, sourceFile, handle);

			// Report a bug that mentions each of the failed arguments in matches

			if (parmType instanceof GenericObjectType)
				parmType = ((GenericObjectType)parmType).getUpperBound();

			int priority = matchResult.getPriority();
			XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
			if (TestCaseDetector.likelyTestCase(xmethod))
				priority = Priorities.LOW_PRIORITY;
			ClassDescriptor expectedClassDescriptor = DescriptorFactory.createClassDescriptorFromSignature(parmType.getSignature());
			ClassDescriptor actualClassDescriptor =DescriptorFactory.createClassDescriptorFromSignature(argType.getSignature());
			if (AnalysisContext.currentAnalysisContext().getClassSummary().mightBeEqualToOtherClasses(actualClassDescriptor))
				priority++;
			accumulator.accumulateBug(new BugInstance(this,
					"GC_UNRELATED_TYPES", priority)
			.addClassAndMethod(methodGen, sourceFile)					
			.addFoundAndExpectedType(argType.getSignature(), parmType.getSignature())
			.addEqualsMethodUsed(actualClassDescriptor)
			.addCalledMethod(methodGen, (InvokeInstruction) ins)
			,sourceLineAnnotation);
		}
		accumulator.reportAccumulatedBugs();
	}

	/**
	 * Compare to see if the argument <code>argType</code> passed to the method 
	 * matches the type of the corresponding parameter. The simplest case is when
	 * both are equal. <p>
	 * This is a conservative comparison - returns true if it cannot decide.
	 * If the parameter type is a type variable (e.g. <code>T</code>) then we don't 
	 * know enough (yet) to decide if they do not match so return true.
	 */
	private IncompatibleTypes compareTypes(Type parmType, Type argType) {
		// XXX equality not implemented for GenericObjectType
		// if (parmType.equals(argType)) return true;
		// Compare type signatures instead
		String parmString = GenericUtilities.getString(parmType);
{'fraction_non_alphanumeric': 0.1075243664717349, 'fraction_numerical': 0.0034307992202729045, 'mean_word_length': 4.509235395189004, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, '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': '7325025', 'n_tokens_mistral': 8468, 'n_tokens_neox': 7511, 'n_words': 1785}