/**
  * Returns the (non-static) fields that have the invariant annotation and are not yet initialized
  * in a given store.
  */
 public List<VariableTree> getUninitializedInvariantFields(
     Store store,
     TreePath path,
     boolean isStatic,
     List<? extends AnnotationMirror> receiverAnnotations) {
   ClassTree currentClass = TreeUtils.enclosingClass(path);
   List<VariableTree> fields = InitializationChecker.getAllFields(currentClass);
   List<VariableTree> violatingFields = new ArrayList<>();
   AnnotationMirror invariant = getFieldInvariantAnnotation();
   for (VariableTree field : fields) {
     if (isUnused(field, receiverAnnotations)) {
       continue; // don't consider unused fields
     }
     VariableElement fieldElem = TreeUtils.elementFromDeclaration(field);
     if (ElementUtils.isStatic(fieldElem) == isStatic) {
       // Does this field need to satisfy the invariant?
       if (getAnnotatedType(field).hasEffectiveAnnotation(invariant)) {
         // Has the field been initialized?
         if (!store.isFieldInitialized(fieldElem)) {
           violatingFields.add(field);
         }
       }
     }
   }
   return violatingFields;
 }
  public RegexQualifiedTypeFactory(QualifierContext<Regex> checker) {
    super(checker);

    patternCompile =
        TreeUtils.getMethod(
            "java.util.regex.Pattern", "compile", 1, getContext().getProcessingEnvironment());
    patternMatcher =
        TreeUtils.getMethod(
            "java.util.regex.Pattern", "matcher", 1, getContext().getProcessingEnvironment());
  }
 public Element getElement() {
   Element el;
   if (tree instanceof IdentifierTree) {
     el = TreeUtils.elementFromUse((IdentifierTree) tree);
   } else {
     assert tree instanceof VariableTree;
     el = TreeUtils.elementFromDeclaration((VariableTree) tree);
   }
   return el;
 }
  public NullnessVisitor(BaseTypeChecker checker, boolean useFbc) {
    super(checker);

    NONNULL = atypeFactory.NONNULL;
    NULLABLE = atypeFactory.NULLABLE;
    MONOTONIC_NONNULL = atypeFactory.MONOTONIC_NONNULL;
    stringType = elements.getTypeElement("java.lang.String").asType();

    ProcessingEnvironment env = checker.getProcessingEnvironment();
    this.collectionSize = TreeUtils.getMethod(java.util.Collection.class.getName(), "size", 0, env);
    this.collectionToArray =
        TreeUtils.getMethod(java.util.Collection.class.getName(), "toArray", 1, env);

    checkForAnnotatedJdk();
  }
 /**
  * Tests whether a method invocation is an invocation of {@link Comparable#compareTo}.
  *
  * <p>
  *
  * @param node a method invocation node
  * @return true iff {@code node} is a invocation of {@code compareTo()}
  */
 private boolean isInvocationOfCompareTo(MethodInvocationTree node) {
   ExecutableElement method = TreeUtils.elementFromUse(node);
   return (method.getParameters().size() == 1
       && method.getReturnType().getKind() == TypeKind.INT
       // method symbols only have simple names
       && method.getSimpleName().contentEquals("compareTo"));
 }
  protected void setSelfTypeInInitializationCode(
      Tree tree, AnnotatedDeclaredType selfType, TreePath path) {
    ClassTree enclosingClass = TreeUtils.enclosingClass(path);
    Type classType = ((JCTree) enclosingClass).type;
    AnnotationMirror annotation = null;

    // If all fields are committed-only, and they are all initialized,
    // then it is save to switch to @UnderInitialization(CurrentClass).
    if (areAllFieldsCommittedOnly(enclosingClass)) {
      Store store = getStoreBefore(tree);
      if (store != null) {
        List<AnnotationMirror> annos = Collections.emptyList();
        if (getUninitializedInvariantFields(store, path, false, annos).size() == 0) {
          if (useFbc) {
            annotation = createFreeAnnotation(classType);
          } else {
            annotation = createUnclassifiedAnnotation(classType);
          }
        }
      }
    }

    if (annotation == null) {
      annotation = getFreeOrRawAnnotationOfSuperType(classType);
    }
    selfType.replaceAnnotation(annotation);
  }
    @Override
    public Void visitMethodInvocation(MethodInvocationTree tree, AnnotatedTypeMirror type) {
      if (isUnderlyingTypeAValue(type)
          && methodIsStaticallyExecutable(TreeUtils.elementFromUse(tree))) {
        // Get argument values
        List<? extends ExpressionTree> arguments = tree.getArguments();
        ArrayList<List<?>> argValues;
        if (arguments.size() > 0) {
          argValues = new ArrayList<List<?>>();
          for (ExpressionTree argument : arguments) {
            AnnotatedTypeMirror argType = getAnnotatedType(argument);
            List<?> values = getValues(argType, argType.getUnderlyingType());
            if (values.isEmpty()) {
              // values aren't known, so don't try to evaluate the
              // method
              return null;
            }
            argValues.add(values);
          }
        } else {
          argValues = null;
        }

        // Get receiver values
        AnnotatedTypeMirror receiver = getReceiverType(tree);
        List<?> receiverValues;

        if (receiver != null && !ElementUtils.isStatic(TreeUtils.elementFromUse(tree))) {
          receiverValues = getValues(receiver, receiver.getUnderlyingType());
          if (receiverValues.isEmpty()) {
            // values aren't known, so don't try to evaluate the
            // method
            return null;
          }
        } else {
          receiverValues = null;
        }

        // Evaluate method
        List<?> returnValues = evalutator.evaluteMethodCall(argValues, receiverValues, tree);
        AnnotationMirror returnType =
            resultAnnotationHandler(type.getUnderlyingType(), returnValues, tree);
        type.replaceAnnotation(returnType);
      }

      return null;
    }
 /** Case 1: Check for null dereferencing */
 @Override
 public Void visitMemberSelect(MemberSelectTree node, Void p) {
   boolean isType = node.getExpression().getKind() == Kind.PARAMETERIZED_TYPE;
   if (!TreeUtils.isSelfAccess(node) && !isType) {
     checkForNullability(node.getExpression(), DEREFERENCE_OF_NULLABLE);
   }
   return super.visitMemberSelect(node, p);
 }
 @Override
 public Void visitMethod(MethodTree node, AnnotatedTypeMirror p) {
   Void result = super.visitMethod(node, p);
   if (TreeUtils.isConstructor(node)) {
     assert p instanceof AnnotatedExecutableType;
     AnnotatedExecutableType exeType = (AnnotatedExecutableType) p;
     DeclaredType underlyingType = (DeclaredType) exeType.getReturnType().getUnderlyingType();
     AnnotationMirror a = getFreeOrRawAnnotationOfSuperType(underlyingType);
     exeType.getReturnType().replaceAnnotation(a);
   }
   return result;
 }
 /**
  * Returns the (non-static) fields that have the invariant annotation and are initialized in a
  * given store.
  */
 public List<VariableTree> getInitializedInvariantFields(Store store, TreePath path) {
   // TODO: Instead of passing the TreePath around, can we use
   // getCurrentClassTree?
   ClassTree currentClass = TreeUtils.enclosingClass(path);
   List<VariableTree> fields = InitializationChecker.getAllFields(currentClass);
   List<VariableTree> initializedFields = new ArrayList<>();
   AnnotationMirror invariant = getFieldInvariantAnnotation();
   for (VariableTree field : fields) {
     VariableElement fieldElem = TreeUtils.elementFromDeclaration(field);
     if (!ElementUtils.isStatic(fieldElem)) {
       // Does this field need to satisfy the invariant?
       if (getAnnotatedType(field).hasEffectiveAnnotation(invariant)) {
         // Has the field been initialized?
         if (store.isFieldInitialized(fieldElem)) {
           initializedFields.add(field);
         }
       }
     }
   }
   return initializedFields;
 }
 /** Returns true if a class overrides Object.equals */
 private boolean overridesEquals(ClassTree node) {
   List<? extends Tree> members = node.getMembers();
   for (Tree member : members) {
     if (member instanceof MethodTree) {
       MethodTree mTree = (MethodTree) member;
       ExecutableElement enclosing = TreeUtils.elementFromDeclaration(mTree);
       if (overrides(enclosing, Object.class, "equals")) {
         return true;
       }
     }
   }
   return false;
 }
  private boolean isNewArrayInToArray(NewArrayTree node) {
    if (node.getDimensions().size() != 1) {
      return false;
    }

    ExpressionTree dim = node.getDimensions().get(0);
    ProcessingEnvironment env = checker.getProcessingEnvironment();

    if (!TreeUtils.isMethodInvocation(dim, collectionSize, env)) {
      return false;
    }

    ExpressionTree rcvsize = ((MethodInvocationTree) dim).getMethodSelect();
    if (!(rcvsize instanceof MemberSelectTree)) {
      return false;
    }
    rcvsize = ((MemberSelectTree) rcvsize).getExpression();
    if (!(rcvsize instanceof IdentifierTree)) {
      return false;
    }

    Tree encl = getCurrentPath().getParentPath().getLeaf();

    if (!TreeUtils.isMethodInvocation(encl, collectionToArray, env)) {
      return false;
    }

    ExpressionTree rcvtoarray = ((MethodInvocationTree) encl).getMethodSelect();
    if (!(rcvtoarray instanceof MemberSelectTree)) {
      return false;
    }
    rcvtoarray = ((MemberSelectTree) rcvtoarray).getExpression();
    if (!(rcvtoarray instanceof IdentifierTree)) {
      return false;
    }

    return ((IdentifierTree) rcvsize).getName() == ((IdentifierTree) rcvtoarray).getName();
  }
  /*
   * Method to implement the @UsesObjectEquals functionality.
   * If a class is marked @UsesObjectEquals, it must:
   *
   *    -not override .equals(Object)
   *    -be a subclass of Object or another class marked @UsesObjectEquals
   *
   * If a class is not marked @UsesObjectEquals, it must:
   *
   * 	  -not have a superclass marked @UsesObjectEquals
   *
   *
   * @see org.checkerframework.common.basetype.BaseTypeVisitor#visitClass(com.sun.source.tree.ClassTree, java.lang.Object)
   */
  @Override
  public Void visitClass(ClassTree node, Void p) {
    // Looking for an @UsesObjectEquals class declaration

    TypeElement elt = TreeUtils.elementFromDeclaration(node);
    UsesObjectEquals annotation = elt.getAnnotation(UsesObjectEquals.class);

    Tree superClass = node.getExtendsClause();
    Element elmt = null;
    if (superClass != null
        && (superClass instanceof IdentifierTree || superClass instanceof MemberSelectTree)) {
      elmt = TreeUtils.elementFromUse((ExpressionTree) superClass);
    }

    // if it's there, check to make sure does not override equals
    // and supertype is Object or @UsesObjectEquals
    if (annotation != null) {
      // check methods to ensure no .equals
      if (overridesEquals(node)) {
        checker.report(Result.failure("overrides.equals"), node);
      }

      if (!(superClass == null
          || (elmt != null && elmt.getAnnotation(UsesObjectEquals.class) != null))) {
        checker.report(Result.failure("superclass.unmarked"), node);
      }
    } else {
      // the class is not marked @UsesObjectEquals -> make sure its superclass isn't either.
      // this is impossible after design change making @UsesObjectEquals inherited?
      // check left in case of future design change back to non-inherited.
      if (superClass != null
          && (elmt != null && elmt.getAnnotation(UsesObjectEquals.class) != null)) {
        checker.report(Result.failure("superclass.marked"), node);
      }
    }

    return super.visitClass(node, p);
  }
 public void handle(MethodInvocationTree tree, AnnotatedExecutableType method) {
   if (TreeUtils.isMethodInvocation(tree, systemGetProperty, env)) {
     List<? extends ExpressionTree> args = tree.getArguments();
     assert args.size() == 1;
     ExpressionTree arg = args.get(0);
     if (arg.getKind() == Tree.Kind.STRING_LITERAL) {
       String literal = (String) ((LiteralTree) arg).getValue();
       if (systemProperties.contains(literal)) {
         AnnotatedTypeMirror type = method.getReturnType();
         type.replaceAnnotation(factory.NONNULL);
       }
     }
   }
 }
  /**
   * In the first enclosing class, find the top-level member that contains tree. TODO: should we
   * look whether these elements are enclosed within another class that is itself under
   * construction.
   *
   * <p>Are there any other type of top level objects?
   */
  private Tree findTopLevelClassMemberForTree(TreePath path) {
    ClassTree enclosingClass = TreeUtils.enclosingClass(path);
    if (enclosingClass != null) {

      List<? extends Tree> classMembers = enclosingClass.getMembers();
      TreePath searchPath = path;
      while (searchPath.getParentPath() != null && searchPath.getParentPath() != enclosingClass) {
        searchPath = searchPath.getParentPath();
        if (classMembers.contains(searchPath.getLeaf())) {
          return searchPath.getLeaf();
        }
      }
    }
    return null;
  }
  /**
   * Checks that the annotations on the type arguments supplied to a type or a method invocation are
   * within the bounds of the type variables as declared, and issues the
   * "type.argument.type.incompatible" error if they are not.
   *
   * <p>This method used to be visitParameterizedType, which incorrectly handles the main annotation
   * on generic types.
   */
  protected Void visitParameterizedType(AnnotatedDeclaredType type, ParameterizedTypeTree tree) {
    // System.out.printf("TypeValidator.visitParameterizedType: type: %s, tree: %s\n",
    // type, tree);

    if (TreeUtils.isDiamondTree(tree)) return null;

    final TypeElement element = (TypeElement) type.getUnderlyingType().asElement();
    if (checker.shouldSkipUses(element)) return null;

    List<AnnotatedTypeVariable> typevars = atypeFactory.typeVariablesFromUse(type, element);

    visitor.checkTypeArguments(tree, typevars, type.getTypeArguments(), tree.getTypeArguments());

    return null;
  }
  @Override
  protected void commonAssignmentCheck(
      Tree varTree, ExpressionTree valueExp, /*@CompilerMessageKey*/ String errorKey) {

    // allow MonotonicNonNull to be initialized to null at declaration
    if (varTree.getKind() == Tree.Kind.VARIABLE) {
      Element elem = TreeUtils.elementFromDeclaration((VariableTree) varTree);
      if (atypeFactory.fromElement(elem).hasEffectiveAnnotation(MONOTONIC_NONNULL)
          && !checker.getLintOption(
              AbstractNullnessChecker.LINT_NOINITFORMONOTONICNONNULL,
              AbstractNullnessChecker.LINT_DEFAULT_NOINITFORMONOTONICNONNULL)) {
        return;
      }
    }
    super.commonAssignmentCheck(varTree, valueExp, errorKey);
  }
  @Override
  public AnnotatedDeclaredType getSelfType(Tree tree) {
    AnnotatedDeclaredType selfType = super.getSelfType(tree);

    TreePath path = getPath(tree);
    Tree topLevelMember = findTopLevelClassMemberForTree(path);
    if (topLevelMember != null) {
      if (topLevelMember.getKind() != Kind.METHOD
          || TreeUtils.isConstructor((MethodTree) topLevelMember)) {

        setSelfTypeInInitializationCode(tree, selfType, path);
      }
    }

    return selfType;
  }
    @Override
    public Void visitMemberSelect(MemberSelectTree tree, AnnotatedTypeMirror type) {
      if (TreeUtils.isFieldAccess(tree) && isUnderlyingTypeAValue(type)) {
        VariableElement elem = (VariableElement) InternalUtils.symbol(tree);
        Object value = elem.getConstantValue();
        if (value != null) {
          // compile time constant
          type.replaceAnnotation(
              resultAnnotationHandler(
                  type.getUnderlyingType(), Collections.singletonList(value), tree));
          return null;
        }
        if (ElementUtils.isStatic(elem) && ElementUtils.isFinal(elem)) {
          Element e = InternalUtils.symbol(tree.getExpression());
          if (e != null) {
            String classname = ElementUtils.getQualifiedClassName(e).toString();
            String fieldName = tree.getIdentifier().toString();
            value = evalutator.evaluateStaticFieldAccess(classname, fieldName, tree);
            if (value != null)
              type.replaceAnnotation(
                  resultAnnotationHandler(
                      type.getUnderlyingType(), Collections.singletonList(value), tree));
            return null;
          }
        }

        if (tree.getIdentifier().toString().equals("length")) {
          AnnotatedTypeMirror receiverType = getAnnotatedType(tree.getExpression());
          if (receiverType.getKind() == TypeKind.ARRAY) {
            AnnotationMirror arrayAnno = receiverType.getAnnotation(ArrayLen.class);
            if (arrayAnno != null) {
              // array.length, where array : @ArrayLen(x)
              List<Integer> lengths = ValueAnnotatedTypeFactory.getArrayLength(arrayAnno);
              type.replaceAnnotation(createNumberAnnotationMirror(new ArrayList<Number>(lengths)));
              return null;
            }
          }
        }
      }
      return null;
    }
  /** Returns whether the field {@code f} is unused, given the annotations on the receiver. */
  private boolean isUnused(
      VariableTree field, Collection<? extends AnnotationMirror> receiverAnnos) {
    if (receiverAnnos.isEmpty()) {
      return false;
    }

    AnnotationMirror unused =
        getDeclAnnotation(TreeUtils.elementFromDeclaration(field), Unused.class);
    if (unused == null) {
      return false;
    }

    Name when = AnnotationUtils.getElementValueClassName(unused, "when", false);
    for (AnnotationMirror anno : receiverAnnos) {
      Name annoName = ((TypeElement) anno.getAnnotationType().asElement()).getQualifiedName();
      if (annoName.contentEquals(when)) {
        return true;
      }
    }

    return false;
  }
 /** Are all fields committed-only? */
 protected boolean areAllFieldsCommittedOnly(ClassTree classTree) {
   if (!useFbc) {
     // In the rawness type system, no fields can store not fully
     // initialized objects.
     return true;
   }
   for (Tree member : classTree.getMembers()) {
     if (!member.getKind().equals(Tree.Kind.VARIABLE)) {
       continue;
     }
     VariableTree var = (VariableTree) member;
     VariableElement varElt = TreeUtils.elementFromDeclaration(var);
     // var is not committed-only
     if (getDeclAnnotation(varElt, NotOnlyInitialized.class) != null) {
       // var is not static -- need a check of initializer blocks,
       // not of constructor which is where this is used
       if (!varElt.getModifiers().contains(Modifier.STATIC)) {
         return false;
       }
     }
   }
   return true;
 }
 @Override
 protected void checkMethodInvocability(
     AnnotatedExecutableType method, MethodInvocationTree node) {
   if (!TreeUtils.isSelfAccess(node)
       &&
       // Static methods don't have a receiver
       method.getReceiverType() != null) {
     // TODO: should all or some constructors be excluded?
     // method.getElement().getKind() != ElementKind.CONSTRUCTOR) {
     Set<AnnotationMirror> recvAnnos = atypeFactory.getReceiverType(node).getAnnotations();
     AnnotatedTypeMirror methodReceiver = method.getReceiverType().getErased();
     AnnotatedTypeMirror treeReceiver = methodReceiver.shallowCopy(false);
     AnnotatedTypeMirror rcv = atypeFactory.getReceiverType(node);
     treeReceiver.addAnnotations(rcv.getEffectiveAnnotations());
     // If receiver is Nullable, then we don't want to issue a warning
     // about method invocability (we'd rather have only the
     // "dereference.of.nullable" message).
     if (treeReceiver.hasAnnotation(NULLABLE) || recvAnnos.contains(MONOTONIC_NONNULL)) {
       return;
     }
   }
   super.checkMethodInvocability(method, node);
 }
    @Override
    public Void visitNewClass(NewClassTree tree, AnnotatedTypeMirror type) {
      boolean wrapperClass =
          TypesUtils.isBoxedPrimitive(type.getUnderlyingType())
              || TypesUtils.isDeclaredOfName(type.getUnderlyingType(), "java.lang.String");

      if (wrapperClass
          || (isUnderlyingTypeAValue(type)
              && methodIsStaticallyExecutable(TreeUtils.elementFromUse(tree)))) {
        // get arugment values
        List<? extends ExpressionTree> arguments = tree.getArguments();
        ArrayList<List<?>> argValues;
        if (arguments.size() > 0) {
          argValues = new ArrayList<List<?>>();
          for (ExpressionTree argument : arguments) {
            AnnotatedTypeMirror argType = getAnnotatedType(argument);
            List<?> values = getValues(argType, argType.getUnderlyingType());
            if (values.isEmpty()) {
              // values aren't known, so don't try to evaluate the
              // method
              return null;
            }
            argValues.add(values);
          }
        } else {
          argValues = null;
        }
        // Evaluate method
        List<?> returnValues =
            evalutator.evaluteConstrutorCall(argValues, tree, type.getUnderlyingType());
        AnnotationMirror returnType =
            resultAnnotationHandler(type.getUnderlyingType(), returnValues, tree);
        type.replaceAnnotation(returnType);
      }

      return null;
    }
  public SystemGetPropertyHandler(ProcessingEnvironment env, NullnessAnnotatedTypeFactory factory) {
    this.env = env;
    this.factory = factory;

    systemGetProperty = TreeUtils.getMethod("java.lang.System", "getProperty", 1, env);
  }
  /**
   * Pattern matches to prevent false positives of the form {@code (a == b || a.compareTo(b) == 0)}.
   * Returns true iff the given node fits this pattern.
   *
   * @param node
   * @return true iff the node fits the pattern (a == b || a.compareTo(b) == 0)
   */
  private boolean suppressEarlyCompareTo(final BinaryTree node) {
    // Only handle == binary trees
    if (node.getKind() != Tree.Kind.EQUAL_TO) return false;

    Tree left = node.getLeftOperand();
    Tree right = node.getRightOperand();

    // Only valid if we're comparing identifiers.
    if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER)) {
      return false;
    }

    final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left);
    final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right);

    // looking for ((a == b || a.compareTo(b) == 0)
    Heuristics.Matcher matcher =
        new Heuristics.Matcher() {

          @Override
          public Boolean visitBinary(BinaryTree tree, Void p) {
            if (tree.getKind() == Tree.Kind.EQUAL_TO) { // a.compareTo(b) == 0
              ExpressionTree leftTree =
                  tree.getLeftOperand(); // looking for a.compareTo(b) or b.compareTo(a)
              ExpressionTree rightTree = tree.getRightOperand(); // looking for 0

              if (rightTree.getKind() != Tree.Kind.INT_LITERAL) {
                return false;
              }
              LiteralTree rightLiteral = (LiteralTree) rightTree;
              if (!rightLiteral.getValue().equals(0)) {
                return false;
              }

              return visit(leftTree, p);
            } else {
              // a == b || a.compareTo(b) == 0
              ExpressionTree leftTree = tree.getLeftOperand(); // looking for a==b
              ExpressionTree rightTree =
                  tree.getRightOperand(); // looking for a.compareTo(b) == 0 or b.compareTo(a) == 0
              if (leftTree != node) {
                return false;
              }
              if (rightTree.getKind() != Tree.Kind.EQUAL_TO) {
                return false;
              }
              return visit(rightTree, p);
            }
          }

          @Override
          public Boolean visitMethodInvocation(MethodInvocationTree tree, Void p) {
            if (!isInvocationOfCompareTo(tree)) {
              return false;
            }

            List<? extends ExpressionTree> args = tree.getArguments();
            if (args.size() != 1) {
              return false;
            }
            ExpressionTree arg = args.get(0);
            if (arg.getKind() != Tree.Kind.IDENTIFIER) {
              return false;
            }
            Element argElt = TreeUtils.elementFromUse(arg);

            ExpressionTree exp = tree.getMethodSelect();
            if (exp.getKind() != Tree.Kind.MEMBER_SELECT) {
              return false;
            }
            MemberSelectTree member = (MemberSelectTree) exp;
            if (member.getExpression().getKind() != Tree.Kind.IDENTIFIER) {
              return false;
            }

            Element refElt = TreeUtils.elementFromUse(member.getExpression());

            if (!((refElt.equals(lhs) && argElt.equals(rhs))
                || ((refElt.equals(rhs) && argElt.equals(lhs))))) {
              return false;
            }
            return true;
          }
        };

    boolean okay =
        Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.CONDITIONAL_OR, matcher))
            .match(getCurrentPath());
    return okay;
  }
  // TODO: handle != comparisons too!
  private boolean suppressInsideComparison(final BinaryTree node) {
    // Only handle == binary trees
    if (node.getKind() != Tree.Kind.EQUAL_TO) return false;

    Tree left = node.getLeftOperand();
    Tree right = node.getRightOperand();

    // Only valid if we're comparing identifiers.
    if (!(left.getKind() == Tree.Kind.IDENTIFIER && right.getKind() == Tree.Kind.IDENTIFIER))
      return false;

    // If we're not directly in an if statement in a method (ignoring
    // parens and blocks), terminate.
    // TODO: only if it's the first statement in the block
    if (!Heuristics.matchParents(getCurrentPath(), Tree.Kind.IF, Tree.Kind.METHOD)) return false;

    ExecutableElement enclosing = TreeUtils.elementFromDeclaration(visitorState.getMethodTree());
    assert enclosing != null;

    final Element lhs = TreeUtils.elementFromUse((IdentifierTree) left);
    final Element rhs = TreeUtils.elementFromUse((IdentifierTree) right);

    // Matcher to check for if statement that returns zero
    Heuristics.Matcher matcher =
        new Heuristics.Matcher() {

          @Override
          public Boolean visitIf(IfTree tree, Void p) {
            return visit(tree.getThenStatement(), p);
          }

          @Override
          public Boolean visitBlock(BlockTree tree, Void p) {
            if (tree.getStatements().size() > 0) return visit(tree.getStatements().get(0), p);
            return false;
          }

          @Override
          public Boolean visitReturn(ReturnTree tree, Void p) {
            ExpressionTree expr = tree.getExpression();
            return (expr != null
                && expr.getKind() == Tree.Kind.INT_LITERAL
                && ((LiteralTree) expr).getValue().equals(0));
          }
        };

    // Determine whether or not the "then" statement of the if has a single
    // "return 0" statement (for the Comparator.compare heuristic).
    if (overrides(enclosing, Comparator.class, "compare")) {
      final boolean returnsZero =
          Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.IF, matcher))
              .match(getCurrentPath());

      if (!returnsZero) return false;

      assert enclosing.getParameters().size() == 2;
      Element p1 = enclosing.getParameters().get(0);
      Element p2 = enclosing.getParameters().get(1);
      return (p1.equals(lhs) && p2.equals(rhs)) || (p2.equals(lhs) && p1.equals(rhs));

    } else if (overrides(enclosing, Object.class, "equals")) {
      assert enclosing.getParameters().size() == 1;
      Element param = enclosing.getParameters().get(0);
      Element thisElt = getThis(trees.getScope(getCurrentPath()));
      assert thisElt != null;
      return (thisElt.equals(lhs) && param.equals(rhs))
          || (param.equals(lhs) && thisElt.equals(rhs));

    } else if (overrides(enclosing, Comparable.class, "compareTo")) {

      final boolean returnsZero =
          Heuristics.Matchers.withIn(Heuristics.Matchers.ofKind(Tree.Kind.IF, matcher))
              .match(getCurrentPath());

      if (!returnsZero) {
        return false;
      }

      assert enclosing.getParameters().size() == 1;
      Element param = enclosing.getParameters().get(0);
      Element thisElt = getThis(trees.getScope(getCurrentPath()));
      assert thisElt != null;
      return (thisElt.equals(lhs) && param.equals(rhs))
          || (param.equals(lhs) && thisElt.equals(rhs));
    }
    return false;
  }