@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; }
@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; }
/** * Fixes the error by assigning the result of the call to the receiver reference, or deleting the * method call. */ public Description describe(MethodInvocationTree methodInvocationTree, VisitorState state) { // Find the root of the field access chain, i.e. a.intern().trim() ==> a. ExpressionTree identifierExpr = ASTHelpers.getRootAssignable(methodInvocationTree); String identifierStr = null; Type identifierType = null; if (identifierExpr != null) { identifierStr = identifierExpr.toString(); if (identifierExpr instanceof JCIdent) { identifierType = ((JCIdent) identifierExpr).sym.type; } else if (identifierExpr instanceof JCFieldAccess) { identifierType = ((JCFieldAccess) identifierExpr).sym.type; } else { throw new IllegalStateException("Expected a JCIdent or a JCFieldAccess"); } } Type returnType = ASTHelpers.getReturnType(((JCMethodInvocation) methodInvocationTree).getMethodSelect()); Fix fix; if (identifierStr != null && !"this".equals(identifierStr) && returnType != null && state.getTypes().isAssignable(returnType, identifierType)) { // Fix by assigning the assigning the result of the call to the root receiver reference. fix = SuggestedFix.prefixWith(methodInvocationTree, identifierStr + " = "); } else { // Unclear what the programmer intended. Delete since we don't know what else to do. Tree parent = state.getPath().getParentPath().getLeaf(); fix = SuggestedFix.delete(parent); } return describeMatch(methodInvocationTree, fix); }
/** Returns true if {@code erasure(s) == erasure(t)}. */ public static boolean isSameType(Type s, Type t, VisitorState state) { if (s == null || t == null) { return false; } Types types = state.getTypes(); return types.isSameType(types.erasure(s), types.erasure(t)); }
/** Return true if the given type is 'void' or 'Void'. */ public static boolean isVoidType(Type type, VisitorState state) { if (type == null) { return false; } return type.getKind() == TypeKind.VOID || state.getTypes().isSameType(Suppliers.JAVA_LANG_VOID_TYPE.get(state), type); }
/** Check if the method declares or inherits an implementation of .equals() */ public static boolean implementsEquals(Type type, VisitorState state) { Name equalsName = state.getName("equals"); Symbol objectEquals = getOnlyMember(state, state.getSymtab().objectType, "equals"); for (Type sup : state.getTypes().closure(type)) { if (sup.tsym.isInterface()) { continue; } if (ASTHelpers.isSameType(sup, state.getSymtab().objectType, state)) { return false; } WriteableScope scope = sup.tsym.members(); if (scope == null) { continue; } for (Symbol sym : scope.getSymbolsByName(equalsName)) { if (sym.overrides(objectEquals, type.tsym, state.getTypes(), false)) { 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; }