@Override public Description matchMethod(MethodTree methodTree, VisitorState state) { // if method is itself annotated with @Inject or it has no ancestor methods, return NO_MATCH; if (hasInjectAnnotation().matches(methodTree, state)) { return Description.NO_MATCH; } boolean foundJavaxInject = false; for (MethodSymbol superMethod : ASTHelpers.findSuperMethods(ASTHelpers.getSymbol(methodTree), state.getTypes())) { // With a Guice annotation, Guice will still inject the subclass-overridden method. if (ASTHelpers.hasAnnotation(superMethod, GUICE_INJECT_ANNOTATION, state)) { return Description.NO_MATCH; } // is not necessarily a match even if we find javax Inject on an ancestor // since a higher up ancestor may have @com.google.inject.Inject foundJavaxInject |= ASTHelpers.hasAnnotation(superMethod, JAVAX_INJECT_ANNOTATION, state); } if (foundJavaxInject) { return describeMatch( methodTree, SuggestedFix.builder() .addImport(JAVAX_INJECT_ANNOTATION) .prefixWith(methodTree, "@Inject\n") .build()); } return Description.NO_MATCH; }
@Override public Description matchClass(ClassTree classTree, VisitorState state) { TypeSymbol symbol = ASTHelpers.getSymbol(classTree); if (symbol.getKind() != ElementKind.CLASS) { return Description.NO_MATCH; } MethodTree equals = null; for (Tree member : classTree.getMembers()) { if (!(member instanceof MethodTree)) { continue; } MethodTree methodTree = (MethodTree) member; if (EQUALS_MATCHER.matches(methodTree, state)) { equals = methodTree; } } if (equals == null) { return Description.NO_MATCH; } MethodSymbol hashCodeSym = ASTHelpers.resolveExistingMethod( state, symbol, state.getName("hashCode"), ImmutableList.<Type>of(), ImmutableList.<Type>of()); if (hashCodeSym.owner.equals(state.getSymtab().objectType.tsym)) { return describeMatch(equals); } return Description.NO_MATCH; }
@Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(expressionTree); if (sym == null) { return false; } if (!(sym instanceof MethodSymbol)) { throw new IllegalArgumentException( "DescendantOf matcher expects a method call but found " + sym.getClass() + ". Expression: " + expressionTree); } if (sym.isStatic()) { return false; } if (methodName.equals(sym.toString())) { Type accessedReferenceType = sym.owner.type; Type collectionType = state.getTypeFromString(fullClassName); if (collectionType != null) { return state .getTypes() .isSubtype(accessedReferenceType, state.getTypes().erasure(collectionType)); } } return false; }
// TODO(eaftan): refactor other code that accesses symbols to use this method public static Symbol getSymbol(Tree tree) { if (tree instanceof ClassTree) { return getSymbol((ClassTree) tree); } if (tree instanceof MethodTree) { return getSymbol((MethodTree) tree); } if (tree instanceof VariableTree) { return getSymbol((VariableTree) tree); } if (tree instanceof JCFieldAccess) { return ((JCFieldAccess) tree).sym; } if (tree instanceof JCIdent) { return ((JCIdent) tree).sym; } if (tree instanceof JCMethodInvocation) { return ASTHelpers.getSymbol((MethodInvocationTree) tree); } if (tree instanceof JCNewClass) { return ((JCNewClass) tree).constructor; } if (tree instanceof AnnotationTree) { return getSymbol(((AnnotationTree) tree).getAnnotationType()); } if (tree instanceof PackageTree) { return getSymbol((PackageTree) tree); } return null; }
/** Gets the symbol for a method invocation. */ public static MethodSymbol getSymbol(MethodInvocationTree tree) { Symbol sym = ASTHelpers.getSymbol(tree.getMethodSelect()); if (!(sym instanceof MethodSymbol)) { // Defensive. Would only occur if there are errors in the AST. return null; } return (MethodSymbol) sym; }
@Override @Nullable protected Unifier defaultAction(Tree node, @Nullable Unifier unifier) { Symbol symbol = ASTHelpers.getSymbol(node); if (symbol != null && symbol.getEnclosingElement() != null && symbol .getEnclosingElement() .getQualifiedName() .contentEquals(classIdent().getQualifiedName()) && symbol.getSimpleName().contentEquals(member())) { return memberType().unify(symbol.asType(), unifier); } return null; }
@Override public Optional<MatchState> matchResult(ExpressionTree tree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(tree); if (!(sym instanceof MethodSymbol)) { return Optional.absent(); } if (tree instanceof NewClassTree) { // Don't match constructors as they are neither static nor instance methods. return Optional.absent(); } if (tree instanceof MethodInvocationTree) { tree = ((MethodInvocationTree) tree).getMethodSelect(); } return Optional.of( MatchState.create(ASTHelpers.getReceiverType(tree), (MethodSymbol) sym)); }
@Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(expressionTree); if (sym != null && sym.getSimpleName().toString().startsWith("log")) { return true; } if (sym != null && sym.isStatic()) { if (sym.owner.getQualifiedName().toString().contains("Logger")) { return true; } } else if (expressionTree instanceof MemberSelectTree) { if (((MemberSelectTree) expressionTree).getExpression().toString().startsWith("log")) { return true; } } return false; }
private boolean inEqualsOrCompareTo(Type classType, Type type, VisitorState state) { MethodTree methodTree = ASTHelpers.findEnclosingNode(state.getPath(), MethodTree.class); if (methodTree == null) { return false; } MethodSymbol sym = ASTHelpers.getSymbol(methodTree); if (sym == null || sym.isStatic()) { return false; } Symbol compareTo = getOnlyMember(state, state.getSymtab().comparableType, "compareTo"); Symbol equals = getOnlyMember(state, state.getSymtab().objectType, "equals"); if (!sym.overrides(compareTo, classType.tsym, state.getTypes(), false) && !sym.overrides(equals, classType.tsym, state.getTypes(), false)) { return false; } if (!ASTHelpers.isSameType(type, classType, state)) { return false; } return true; }
@Override public boolean matches(ExpressionTree expressionTree, VisitorState state) { Symbol sym = ASTHelpers.getSymbol(expressionTree); return sym != null && sym.getSimpleName().toString().startsWith("assert"); }