Example #1
0
  @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);
  }
Example #4
0
 /** 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));
 }
Example #5
0
 /** 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);
 }
Example #6
0
 /** 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;
 }
Example #7
0
 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;
 }