Esempio n. 1
0
 public static String getOrGuessSourceFile(SourceLineAnnotation source) {
   if (source.isSourceFileKnown()) return source.getSourceFile();
   String baseClassName = source.getClassName();
   int i = baseClassName.lastIndexOf('.');
   baseClassName = baseClassName.substring(i + 1);
   int j = baseClassName.indexOf("$");
   if (j >= 0) baseClassName = baseClassName.substring(0, j);
   return baseClassName + ".java";
 }
 private void setAffectedLines(final BugInstance warning, final Bug bug) {
   Iterator<BugAnnotation> annotationIterator = warning.annotationIterator();
   while (annotationIterator.hasNext()) {
     BugAnnotation bugAnnotation = annotationIterator.next();
     if (bugAnnotation instanceof SourceLineAnnotation) {
       SourceLineAnnotation annotation = (SourceLineAnnotation) bugAnnotation;
       bug.addLineRange(new LineRange(annotation.getStartLine(), annotation.getEndLine()));
     }
   }
 }
 private String findSourceFile(
     final Project project,
     final SourceFinder sourceFinder,
     final SourceLineAnnotation sourceLine) {
   try {
     SourceFile sourceFile = sourceFinder.findSourceFile(sourceLine);
     return sourceFile.getFullFileName();
   } catch (IOException exception) {
     StringBuilder sb = new StringBuilder("Can't resolve absolute file name for file ");
     sb.append(sourceLine.getSourceFile());
     if (isFirstError) {
       sb.append(", dir list = ");
       sb.append(project.getSourceDirList());
       isFirstError = false;
     }
     Logger.getLogger(getClass().getName()).log(Level.WARNING, sb.toString());
     return sourceLine.getPackageName().replace(DOT, SLASH) + SLASH + sourceLine.getSourceFile();
   }
 }
Esempio n. 4
0
  private void analyzeMethod(
      ClassContext classContext, Method method, Collection<InjectionSource> selectedSources)
      throws DataflowAnalysisException, CheckedAnalysisException {
    TaintDataflow dataflow = getTaintDataFlow(classContext, method);
    ConstantPoolGen cpg = classContext.getConstantPoolGen();
    String currentMethod = getFullMethodName(classContext.getMethodGen(method));
    for (Iterator<Location> i = getLocationIterator(classContext, method); i.hasNext(); ) {
      Location location = i.next();
      InstructionHandle handle = location.getHandle();
      Instruction instruction = handle.getInstruction();
      if (!(instruction instanceof InvokeInstruction)) {
        continue;
      }
      InvokeInstruction invoke = (InvokeInstruction) instruction;
      TaintFrame fact = dataflow.getFactAtLocation(location);
      assert fact != null;
      if (!fact.isValid()) {
        continue;
      }
      SourceLineAnnotation sourceLine =
          SourceLineAnnotation.fromVisitedInstruction(classContext, method, handle);
      checkTaintSink(getFullMethodName(cpg, invoke), fact, sourceLine, currentMethod);
      InjectionPoint injectionPoint = getInjectionPoint(invoke, cpg, handle, selectedSources);
      for (int offset : injectionPoint.getInjectableArguments()) {

        Taint parameterTaint = fact.getStackValue(offset);

        int priority = getPriority(parameterTaint);
        if (priority == Priorities.IGNORE_PRIORITY) {
          continue;
        }
        BugInstance bugInstance = new BugInstance(this, injectionPoint.getBugType(), priority);

        bugInstance.addClassAndMethod(classContext.getJavaClass(), method);
        bugInstance.addSourceLine(sourceLine);
        if (injectionPoint.getInjectableMethod() != null) {
          bugInstance.addString(injectionPoint.getInjectableMethod());
        }
        reportBug(bugInstance, parameterTaint, currentMethod);
      }
    }
  }
Esempio n. 5
0
 private static void addSourceLines(Collection<TaintLocation> locations, BugInstance bugInstance) {
   List<SourceLineAnnotation> annotations = new LinkedList<SourceLineAnnotation>();
   for (TaintLocation location : locations) {
     SourceLineAnnotation taintSource =
         SourceLineAnnotation.fromVisitedInstruction(
             location.getMethodDescriptor(), location.getPosition());
     annotations.add(taintSource);
   }
   Collections.sort(annotations);
   SourceLineAnnotation annotation = null;
   for (Iterator<SourceLineAnnotation> it = annotations.iterator(); it.hasNext(); ) {
     SourceLineAnnotation prev = annotation;
     annotation = it.next();
     if (prev != null
         && prev.getClassName().equals(annotation.getClassName())
         && prev.getStartLine() == annotation.getStartLine()) {
       // keep only one annotation per line
       it.remove();
     }
   }
   for (SourceLineAnnotation sourceLine : annotations) {
     bugInstance.addSourceLine(sourceLine);
   }
 }
  /**
   * Returns the parsed FindBugs analysis file. This scanner accepts files in the native FindBugs
   * format.
   *
   * @param file the FindBugs analysis file
   * @param sources a collection of folders to scan for source files
   * @param moduleName name of maven module
   * @param hashToMessageMapping mapping of hash codes to messages
   * @param categories mapping from bug types to their categories
   * @return the parsed result (stored in the module instance)
   * @throws IOException if the file could not be parsed
   * @throws DocumentException in case of a parser exception
   */
  private Collection<FileAnnotation> parse(
      final InputStream file,
      final Collection<String> sources,
      final String moduleName,
      final Map<String, String> hashToMessageMapping,
      final Map<String, String> categories)
      throws IOException, DocumentException {
    SortedBugCollection collection = readXml(file);

    Project project = collection.getProject();
    for (String sourceFolder : sources) {
      project.addSourceDir(sourceFolder);
    }

    SourceFinder sourceFinder = new SourceFinder(project);
    String actualName = extractModuleName(moduleName, project);

    TreeStringBuilder stringPool = new TreeStringBuilder();
    List<FileAnnotation> annotations = new ArrayList<FileAnnotation>();
    Collection<BugInstance> bugs = collection.getCollection();

    for (BugInstance warning : bugs) {

      SourceLineAnnotation sourceLine = warning.getPrimarySourceLineAnnotation();

      String message = warning.getMessage();
      String type = warning.getType();
      if (message.contains("TEST: Unknown")) {
        message = FindBugsMessages.getInstance().getShortMessage(type, LocaleProvider.getLocale());
      }
      String category = categories.get(type);
      if (category
          == null) { // alternately, only if warning.getBugPattern().getType().equals("UNKNOWN")
        category = warning.getBugPattern().getCategory();
      }
      Bug bug =
          new Bug(
              getPriority(warning),
              StringUtils.defaultIfEmpty(
                  hashToMessageMapping.get(warning.getInstanceHash()), message),
              category,
              type,
              sourceLine.getStartLine(),
              sourceLine.getEndLine());
      bug.setInstanceHash(warning.getInstanceHash());
      bug.setRank(warning.getBugRank());

      boolean ignore = setCloudInformation(collection, warning, bug);
      if (!ignore) {
        bug.setNotAProblem(false);
        bug.setFileName(findSourceFile(project, sourceFinder, sourceLine));
        bug.setPackageName(warning.getPrimaryClass().getPackageName());
        bug.setModuleName(actualName);
        setAffectedLines(warning, bug);

        annotations.add(bug);
        bug.intern(stringPool);
      }
    }

    return applyFilters(annotations);
  }
  /**
   * implements the visitor to find stores to locals of synchronized collections
   *
   * @param seen the opcode of the currently parsed instruction
   */
  @Override
  public void sawOpcode(int seen) {
    Integer tosIsSyncColReg = null;
    try {
      stack.mergeJumps(this);

      if (seen == INVOKESPECIAL) {
        if ("<init>".equals(getNameConstantOperand())) {
          Integer minVersion = syncCtors.get(getClassConstantOperand());
          if ((minVersion != null) && (classVersion >= minVersion.intValue())) {
            tosIsSyncColReg = Integer.valueOf(-1);
          }
        }
      } else if (seen == INVOKESTATIC) {
        if ("java/util/Collections".equals(getClassConstantOperand())) {
          if (syncMethods.contains(getNameConstantOperand())) {
            tosIsSyncColReg = Integer.valueOf(-1);
          }
        }
      } else if ((seen == ASTORE) || ((seen >= ASTORE_0) && (seen <= ASTORE_3))) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          int reg = RegisterUtils.getAStoreReg(this, seen);
          if (item.getUserValue() != null) {
            if (!syncRegs.containsKey(Integer.valueOf(reg))) {
              CollectionRegInfo cri =
                  new CollectionRegInfo(
                      SourceLineAnnotation.fromVisitedInstruction(this),
                      RegisterUtils.getLocalVariableEndRange(
                          getMethod().getLocalVariableTable(), reg, getNextPC()));
              syncRegs.put(Integer.valueOf(reg), cri);
            }
          } else {
            CollectionRegInfo cri = syncRegs.get(Integer.valueOf(reg));
            if (cri == null) {
              cri =
                  new CollectionRegInfo(
                      RegisterUtils.getLocalVariableEndRange(
                          getMethod().getLocalVariableTable(), reg, getNextPC()));
              syncRegs.put(Integer.valueOf(reg), cri);
            }
            cri.setIgnore();
          }
        }
      } else if ((seen == ALOAD) || ((seen >= ALOAD_0) && (seen <= ALOAD_3))) {
        int reg = RegisterUtils.getALoadReg(this, seen);
        CollectionRegInfo cri = syncRegs.get(Integer.valueOf(reg));
        if ((cri != null) && !cri.getIgnore()) tosIsSyncColReg = Integer.valueOf(reg);
      } else if ((seen == PUTFIELD) || (seen == ARETURN)) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          syncRegs.remove(item.getUserValue());
        }
      }

      if (syncRegs.size() > 0) {
        if ((seen == INVOKESPECIAL)
            || (seen == INVOKEINTERFACE)
            || (seen == INVOKEVIRTUAL)
            || (seen == INVOKESTATIC)) {
          String sig = getSigConstantOperand();
          int argCount = Type.getArgumentTypes(sig).length;
          if (stack.getStackDepth() >= argCount) {
            for (int i = 0; i < argCount; i++) {
              OpcodeStack.Item item = stack.getStackItem(i);
              CollectionRegInfo cri = syncRegs.get(item.getUserValue());
              if (cri != null) cri.setPriority(LOW_PRIORITY);
            }
          }
        } else if (seen == MONITORENTER) {
          // Assume if synchronized blocks are used then something tricky is going on.
          // There is really no valid reason for this, other than folks who use
          // synchronized blocks tend to know what's going on.
          if (stack.getStackDepth() > 0) {
            OpcodeStack.Item item = stack.getStackItem(0);
            syncRegs.remove(item.getUserValue());
          }
        } else if (seen == AASTORE) {
          if (stack.getStackDepth() > 0) {
            OpcodeStack.Item item = stack.getStackItem(0);
            syncRegs.remove(item.getUserValue());
          }
        }
      }

      int curPC = getPC();
      Iterator<CollectionRegInfo> it = syncRegs.values().iterator();
      while (it.hasNext()) {
        CollectionRegInfo cri = it.next();
        if (cri.getEndPCRange() < curPC) {
          if (!cri.getIgnore()) {
            bugReporter.reportBug(
                new BugInstance(this, "LSYC_LOCAL_SYNCHRONIZED_COLLECTION", cri.getPriority())
                    .addClass(this)
                    .addMethod(this)
                    .addSourceLine(cri.getSourceLineAnnotation()));
          }
          it.remove();
        }
      }
    } finally {
      TernaryPatcher.pre(stack, seen);
      stack.sawOpcode(this, seen);
      TernaryPatcher.post(stack, seen);
      if (tosIsSyncColReg != null) {
        if (stack.getStackDepth() > 0) {
          OpcodeStack.Item item = stack.getStackItem(0);
          item.setUserValue(tosIsSyncColReg);
        }
      }
    }
  }
Esempio n. 8
0
  public static List<ErrorDescription> runFindBugs(
      CompilationInfo info,
      Preferences customSettings,
      String singleBug,
      FileObject sourceRoot,
      Iterable<? extends String> classNames,
      FindBugsProgress progress,
      SigFilesValidator validator) {
    List<ErrorDescription> result = new ArrayList<ErrorDescription>();

    try {
      Class.forName(
          "org.netbeans.modules.findbugs.NbClassFactory",
          true,
          RunFindBugs.class.getClassLoader()); // NOI18N
      Project p = new Project();
      URL[] binaryRoots =
          CacheBinaryForSourceQuery.findCacheBinaryRoots(sourceRoot.toURL()).getRoots();

      if (classNames == null) {
        for (URL binary : binaryRoots) {
          try {
            p.addFile(new File(binary.toURI()).getAbsolutePath());
          } catch (URISyntaxException ex) {
            Exceptions.printStackTrace(ex);
          }
        }
      } else {
        ClassPath binary = ClassPathSupport.createClassPath(binaryRoots);
        List<FileObject> sigFiles = new ArrayList<FileObject>();

        for (String className : classNames) {
          FileObject classFO = binary.findResource(className.replace('.', '/') + ".sig"); // NOI18N

          if (classFO != null) {
            sigFiles.add(classFO);
          } else {
            LOG.log(
                Level.WARNING,
                "Cannot find sig file for: "
                    + className); // TODO: should probably become FINE eventually
          }
        }

        assert validator != null;

        if (!validator.validate(sigFiles)) return null;

        for (FileObject classFO : sigFiles) {
          p.addFile(new File(classFO.toURI()).getAbsolutePath());
        }

        addCompileRootAsSource(p, sourceRoot);
      }

      ClassPath compile = ClassPath.getClassPath(sourceRoot, ClassPath.COMPILE);

      for (FileObject compileRoot : compile.getRoots()) {
        addCompileRoot(p, compileRoot);
      }

      BugCollectionBugReporter r =
          new BugCollectionBugReporter(p) {
            @Override
            protected void emitLine(String line) {
              LOG.log(Level.FINE, line);
            }
          };

      r.setPriorityThreshold(Integer.MAX_VALUE);
      r.setRankThreshold(Integer.MAX_VALUE);

      FindBugs2 engine = new FindBugs2();

      engine.setProject(p);
      engine.setNoClassOk(true);
      engine.setBugReporter(r);

      if (progress != null) {
        engine.setProgressCallback(progress);
      }

      boolean inEditor = validator != null;
      Preferences settings =
          customSettings != null
              ? customSettings
              : NbPreferences.forModule(RunFindBugs.class).node("global-settings");
      UserPreferences preferences;

      if (singleBug != null) {
        singleBug = singleBug.substring(PREFIX_FINDBUGS.length());
        preferences = forSingleBug(singleBug);
      } else {
        preferences = readPreferences(settings, customSettings != null);
      }

      if (preferences == null) {
        // nothing enabled, stop
        return result;
      }

      engine.setUserPreferences(preferences);
      engine.setDetectorFactoryCollection(DetectorFactoryCollection.instance());

      LOG.log(Level.FINE, "Running FindBugs");

      engine.execute();

      Map<FileObject, List<BugInstance>> file2Bugs = new HashMap<FileObject, List<BugInstance>>();

      for (BugInstance b : r.getBugCollection().getCollection()) {
        if (singleBug != null && !singleBug.equals(b.getBugPattern().getType())) continue;
        if (singleBug == null
            && !settings.getBoolean(
                b.getBugPattern().getType(),
                customSettings == null && isEnabledByDefault(b.getBugPattern()))) {
          continue;
        }

        SourceLineAnnotation sourceLine = b.getPrimarySourceLineAnnotation();
        FileObject sourceFile = null;

        if (sourceLine != null) {
          sourceFile = sourceRoot.getFileObject(sourceLine.getSourcePath());

          if (sourceFile != null) {
            List<BugInstance> bugs = file2Bugs.get(sourceFile);

            if (bugs == null) {
              file2Bugs.put(sourceFile, bugs = new ArrayList<BugInstance>());
            }

            bugs.add(b);
          } else {
            LOG.log(
                Level.WARNING,
                "{0}, location: {1}:{2}",
                new Object[] {b, sourceLine.getSourcePath(), sourceLine.getStartLine()});
          }
        }
      }

      for (Entry<FileObject, List<BugInstance>> e : file2Bugs.entrySet()) {
        int[] lineOffsets = null;
        FileObject sourceFile = e.getKey();
        DataObject d = DataObject.find(sourceFile);
        EditorCookie ec = d.getLookup().lookup(EditorCookie.class);
        Document doc = ec.getDocument();
        JavaSource js = null;

        for (BugInstance b : e.getValue()) {
          SourceLineAnnotation sourceLine = b.getPrimarySourceLineAnnotation();

          if (sourceLine.getStartLine() >= 0) {
            LazyFixList fixes =
                prepareFixes(b, inEditor, sourceFile, sourceLine.getStartLine(), null);

            if (doc != null) {
              result.add(
                  ErrorDescriptionFactory.createErrorDescription(
                      PREFIX_FINDBUGS + b.getType(),
                      Severity.VERIFIER,
                      b.getMessageWithoutPrefix(),
                      b.getBugPattern().getDetailHTML(),
                      fixes,
                      doc,
                      sourceLine.getStartLine()));
            } else {
              if (lineOffsets == null) {
                lineOffsets = computeLineMap(sourceFile, FileEncodingQuery.getEncoding(sourceFile));
              }

              int edLine = 2 * (Math.min(sourceLine.getStartLine(), lineOffsets.length / 2) - 1);

              result.add(
                  ErrorDescriptionFactory.createErrorDescription(
                      PREFIX_FINDBUGS + b.getType(),
                      Severity.VERIFIER,
                      b.getMessageWithoutPrefix(),
                      b.getBugPattern().getDetailHTML(),
                      fixes,
                      sourceFile,
                      lineOffsets[edLine],
                      lineOffsets[edLine + 1]));
            }
          } else {
            if (js == null) {
              js = JavaSource.forFileObject(sourceFile);
            }
            addByElementAnnotation(b, info, sourceFile, js, result, inEditor);
          }
        }
      }
    } catch (ClassNotFoundException ex) {
      Exceptions.printStackTrace(ex);
    } catch (IOException ex) {
      Exceptions.printStackTrace(ex);
    } catch (InterruptedException ex) {
      LOG.log(Level.FINE, null, ex);
    }

    return result;
  }
Esempio n. 9
0
 public boolean hasSourceFile(SourceLineAnnotation source) {
   return hasSourceFile(source.getPackageName(), getOrGuessSourceFile(source));
 }
Esempio n. 10
0
 public static String getCanonicalName(SourceLineAnnotation source) {
   return getCanonicalName(source.getPackageName(), getOrGuessSourceFile(source));
 }
Esempio n. 11
0
 public SourceFile findSourceFile(SourceLineAnnotation source) throws IOException {
   return findSourceFile(source.getPackageName(), getOrGuessSourceFile(source));
 }
  private void analyzeMethod(ClassContext classContext, Method method)
      throws CFGBuilderException, DataflowAnalysisException {
    if (isSynthetic(method) || !prescreen(classContext, method)) return;
    XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
    if (xmethod.isSynthetic()) return;

    BugAccumulator accumulator = new BugAccumulator(bugReporter);

    CFG cfg = classContext.getCFG(method);
    TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
    ValueNumberDataflow vnDataflow = classContext.getValueNumberDataflow(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("\n" + 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 invokedMethod = XFactory.createXMethod(inv, cpg);

      String invokedMethodName = invokedMethod.getName();
      String argSignature = invokedMethod.getSignature();
      argSignature = argSignature.substring(0, argSignature.indexOf(')') + 1);
      String call = invokedMethodName + argSignature;
      SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg));

      Collection<Info> collection = callMap.get(call);
      if (!callMap.containsKey(call)) continue;
      for (Info info : collection) {
        Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
        if (DEBUG)
          System.out.println(
              "at "
                  + handle.getPosition()
                  + " Checking call to "
                  + info.interfaceForCall
                  + " : "
                  + invokedMethod);
        try {
          if (!subtypes2.isSubtype(invokedMethod.getClassDescriptor(), info.interfaceForCall))
            continue;
        } catch (ClassNotFoundException e) {
          if (info.interfaceForCall.getClassName().equals("java/util/Collection")
              && invokedMethod.getClassName().equals("com.google.common.collect.Multiset")) {
            assert true;
            // we know this is OK without needing to find definition of Multiset
          } else {
            AnalysisContext.reportMissingClass(e);
            continue;
          }
        }

        boolean allMethod;

        int typeArgument;
        if (info.typeIndex >= 0) {
          allMethod = false;
          typeArgument = info.typeIndex;
        } else {
          allMethod = true;
          typeArgument = -(1 + info.typeIndex);
        }
        int pos = info.argumentIndex;

        int lhsPos;
        if (inv instanceof INVOKESTATIC) lhsPos = sigParser.getSlotsFromTopOfStackForParameter(0);
        else lhsPos = sigParser.getTotalArgumentSize();

        int stackPos = sigParser.getSlotsFromTopOfStackForParameter(pos);

        TypeFrame frame = typeDataflow.getFactAtLocation(location);
        if (!frame.isValid()) {
          // This basic block is probably dead
          continue;
        }

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

        if (operandType.equals(NullType.instance())) {
          // ignore
          continue;
        }

        ValueNumberFrame vnFrame = vnDataflow.getFactAtLocation(location);

        if (!vnFrame.isValid()) {
          AnalysisContext.logError("Invalid value number frame in " + xmethod);
          continue;
        }

        ValueNumber objectVN = vnFrame.getStackValue(lhsPos);
        ValueNumber argVN = vnFrame.getStackValue(stackPos);

        if (objectVN.equals(argVN)) {
          String bugPattern = "DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES";
          int priority = HIGH_PRIORITY;
          if (invokedMethodName.equals("removeAll")) {
            bugPattern = "DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION";
            priority = NORMAL_PRIORITY;
          } else if (invokedMethodName.endsWith("All")) {
            bugPattern = "DMI_VACUOUS_SELF_COLLECTION_CALL";
            priority = NORMAL_PRIORITY;
          }
          if (invokedMethodName.startsWith("contains")) {
            InstructionHandle next = handle.getNext();
            if (next != null) {
              Instruction nextIns = next.getInstruction();

              if (nextIns instanceof InvokeInstruction) {
                XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
                if (nextMethod.getName().equals("assertFalse")) continue;
              }
            }
          }
          accumulator.accumulateBug(
              new BugInstance(this, bugPattern, priority)
                  .addClassAndMethod(methodGen, sourceFile)
                  .addCalledMethod(methodGen, (InvokeInstruction) ins)
                  .addOptionalAnnotation(
                      ValueNumberSourceInfo.findAnnotationFromValueNumber(
                          method, location, objectVN, vnFrame, "INVOKED_ON")),
              SourceLineAnnotation.fromVisitedInstruction(
                  classContext, methodGen, sourceFile, handle));
        }

        // Only consider generic...
        Type objectType = frame.getStackValue(lhsPos);
        if (!(objectType instanceof GenericObjectType)) continue;

        GenericObjectType operand = (GenericObjectType) objectType;

        int expectedTypeParameters = 1;
        String simpleName = info.interfaceForCall.getSimpleName();
        if (simpleName.toLowerCase().endsWith("map") || simpleName.equals("Hashtable"))
          expectedTypeParameters = 2;
        else if (simpleName.equals("Table")) expectedTypeParameters = 3;

        // ... containers
        if (!operand.hasParameters()) continue;
        if (operand.getNumParameters() != expectedTypeParameters) continue;
        ClassDescriptor operandClass = DescriptorFactory.getClassDescriptor(operand);
        if (!isGenericCollection(operandClass)) continue;

        if (expectedTypeParameters == 2
            && Subtypes2.instanceOf(operandClass, Map.class)
            && !TypeFrameModelingVisitor.isStraightGenericMap(operandClass)) continue;
        Type expectedType;
        if (allMethod) expectedType = operand;
        else expectedType = operand.getParameterAt(typeArgument);
        Type actualType = frame.getStackValue(stackPos);
        Type equalsType = actualType;
        if (allMethod) {
          if (!(actualType instanceof GenericObjectType)) {
            continue;
          }
          equalsType = ((GenericObjectType) actualType).getParameterAt(typeArgument);
        }

        IncompatibleTypes matchResult = compareTypes(expectedType, actualType, allMethod);

        boolean parmIsObject = expectedType.getSignature().equals("Ljava/lang/Object;");
        boolean selfOperation = !allMethod && operand.equals(actualType) && !parmIsObject;
        if (!allMethod && !parmIsObject && actualType instanceof GenericObjectType) {

          GenericObjectType p2 = (GenericObjectType) actualType;
          List<? extends ReferenceType> parameters = p2.getParameters();
          if (parameters != null && parameters.equals(operand.getParameters()))
            selfOperation = true;
        }

        if (!selfOperation
            && (matchResult == IncompatibleTypes.SEEMS_OK
                || matchResult.getPriority() == Priorities.IGNORE_PRIORITY)) continue;

        if (invokedMethodName.startsWith("contains") || invokedMethodName.equals("remove")) {
          InstructionHandle next = handle.getNext();
          if (next != null) {
            Instruction nextIns = next.getInstruction();

            if (nextIns instanceof InvokeInstruction) {
              XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
              if (nextMethod.getName().equals("assertFalse")) continue;
            }
          }
        } else if (invokedMethodName.equals("get") || invokedMethodName.equals("remove")) {
          InstructionHandle next = handle.getNext();
          if (next != null) {
            Instruction nextIns = next.getInstruction();

            if (nextIns instanceof InvokeInstruction) {
              XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
              if (nextMethod.getName().equals("assertNull")) continue;
            }
          }
        }
        boolean noisy = false;
        if (invokedMethodName.equals("get")) {
          UnconditionalValueDerefDataflow unconditionalValueDerefDataflow =
              classContext.getUnconditionalValueDerefDataflow(method);

          UnconditionalValueDerefSet unconditionalDeref =
              unconditionalValueDerefDataflow.getFactAtLocation(location);
          ValueNumberFrame vnAfter = vnDataflow.getFactAfterLocation(location);
          ValueNumber top = vnAfter.getTopValue();
          noisy =
              unconditionalDeref.getValueNumbersThatAreUnconditionallyDereferenced().contains(top);
        }
        // Prepare bug report
        SourceLineAnnotation sourceLineAnnotation =
            SourceLineAnnotation.fromVisitedInstruction(
                classContext, methodGen, sourceFile, handle);

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

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

        int priority = matchResult.getPriority();
        if (!operandClass.getClassName().startsWith("java/util")
            && priority == Priorities.HIGH_PRIORITY)
          priority = Math.max(priority, Priorities.NORMAL_PRIORITY);
        if (TestCaseDetector.likelyTestCase(xmethod))
          priority = Math.max(priority, Priorities.NORMAL_PRIORITY);
        else if (selfOperation) priority = Priorities.HIGH_PRIORITY;
        ClassDescriptor expectedClassDescriptor =
            DescriptorFactory.createClassOrObjectDescriptorFromSignature(
                expectedType.getSignature());
        ClassDescriptor actualClassDescriptor =
            DescriptorFactory.createClassOrObjectDescriptorFromSignature(equalsType.getSignature());
        ClassSummary classSummary = AnalysisContext.currentAnalysisContext().getClassSummary();
        Set<XMethod> targets = null;
        try {
          targets =
              Hierarchy2.resolveVirtualMethodCallTargets(
                  actualClassDescriptor, "equals", "(Ljava/lang/Object;)Z", false, false);
          boolean allOk = targets.size() > 0;
          for (XMethod m2 : targets)
            if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), expectedClassDescriptor))
              allOk = false;
          if (allOk) priority += 2;
        } catch (ClassNotFoundException e) {
          AnalysisContext.reportMissingClass(e);
        }
        String bugPattern = "GC_UNRELATED_TYPES";

        BugInstance bug =
            new BugInstance(this, bugPattern, priority)
                .addClassAndMethod(methodGen, sourceFile)
                .addFoundAndExpectedType(actualType, expectedType)
                .addCalledMethod(methodGen, (InvokeInstruction) ins)
                .addOptionalAnnotation(
                    ValueNumberSourceInfo.findAnnotationFromValueNumber(
                        method, location, objectVN, vnFrame, "INVOKED_ON"))
                .addOptionalAnnotation(
                    ValueNumberSourceInfo.findAnnotationFromValueNumber(
                        method, location, argVN, vnFrame, "ARGUMENT"))
                .addEqualsMethodUsed(targets);
        if (noisy) {
          WarningPropertySet<WarningProperty> propertySet =
              new WarningPropertySet<WarningProperty>();

          propertySet.addProperty(GeneralWarningProperty.NOISY_BUG);
          propertySet.decorateBugInstance(bug);
        }
        accumulator.accumulateBug(bug, sourceLineAnnotation);
      }
    }
    accumulator.reportAccumulatedBugs();
  }