/**
   * 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);
  }
示例#2
0
 @Override
 protected boolean matchArgument(ExpressionTree tree, VisitorState state) {
   Type type = ASTHelpers.getType(tree);
   if (!type.isReference()) {
     return false;
   }
   ClassTree classTree = ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class);
   if (classTree == null) {
     return false;
   }
   Type classType = ASTHelpers.getType(classTree);
   if (classType == null) {
     return false;
   }
   if (inEqualsOrCompareTo(classType, type, state)) {
     return false;
   }
   if (ASTHelpers.isSubtype(type, state.getSymtab().enumSym.type, state)) {
     return false;
   }
   if (ASTHelpers.isSubtype(type, state.getSymtab().classType, state)) {
     return false;
   }
   if (!implementsEquals(type, state)) {
     return false;
   }
   return true;
 }
示例#3
0
  @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(TryTree tryTree, VisitorState state) {
   return ASTHelpers.findEnclosingNode(state.getPath(), DoWhileLoopTree.class) != null
       || ASTHelpers.findEnclosingNode(state.getPath(), EnhancedForLoopTree.class) != null
       || ASTHelpers.findEnclosingNode(state.getPath(), WhileLoopTree.class) != null
       || ASTHelpers.findEnclosingNode(state.getPath(), ForLoopTree.class) != null;
 }
示例#5
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;
  }
示例#6
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);
 }
示例#7
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));
 }
  @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;
  }
示例#9
0
  /**
   * Determines whether a symbol has an annotation of the given type. This includes annotations
   * inherited from superclasses due to @Inherited.
   *
   * @param annotationType The type of the annotation to look for (e.g, "javax.annotation.Nullable")
   */
  public static boolean hasAnnotation(Symbol sym, String annotationType, VisitorState state) {
    Symbol annotationSym = state.getSymbolFromString(annotationType);
    Symbol inheritedSym = state.getSymtab().inheritedType.tsym;

    if ((sym == null) || (annotationSym == null)) {
      return false;
    }
    if ((sym instanceof ClassSymbol) && (annotationSym.attribute(inheritedSym) != null)) {
      while (sym != null) {
        if (sym.attribute(annotationSym) != null) {
          return true;
        }
        sym = ((ClassSymbol) sym).getSuperclass().tsym;
      }
      return false;
    } else {
      return sym.attribute(annotationSym) != null;
    }
  }
示例#10
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;
 }
示例#11
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;
 }
示例#12
0
    @Override
    public boolean matches(TryTree tryTree, VisitorState state) {
      MethodTree enclosingMethodTree =
          ASTHelpers.findEnclosingNode(state.getPath(), MethodTree.class);

      Name name = enclosingMethodTree.getName();
      return JUnitMatchers.looksLikeJUnit3SetUp.matches(enclosingMethodTree, state)
          || JUnitMatchers.looksLikeJUnit3TearDown.matches(enclosingMethodTree, state)
          || name.contentEquals("main")
          // TODO(schmitt): Move to JUnitMatchers?
          || name.contentEquals("suite")
          || Matchers.hasAnnotation(JUNIT_BEFORE_ANNOTATION).matches(enclosingMethodTree, state)
          || Matchers.hasAnnotation(JUNIT_AFTER_ANNOTATION).matches(enclosingMethodTree, state);
    }
示例#13
0
 private static Symbol getOnlyMember(VisitorState state, Type type, String name) {
   return getOnlyElement(type.tsym.members().getSymbolsByName(state.getName(name)));
 }
示例#14
0
 @Override
 public Type get(VisitorState state) {
   return ((JCTree) state.findEnclosing(ClassTree.class)).type;
 }
示例#15
0
 @Override
 public Type get(VisitorState state) {
   return state.getSymtab().annotationType;
 }
示例#16
0
 @Override
 public Type get(VisitorState state) {
   return state.getSymtab().objectType;
 }
示例#17
0
 @Override
 public Type get(VisitorState state) {
   return state.getSymtab().booleanType;
 }
示例#18
0
 @Override
 public Type get(VisitorState state) {
   return state.getTypeFromString("java.lang.Boolean");
 }
示例#19
0
 /**
  * Returns the {@link Nullness} for an expression as determined by the nullness dataflow analysis.
  */
 public static Nullness getNullnessValue(
     ExpressionTree expr, VisitorState state, NullnessAnalysis nullnessAnalysis) {
   TreePath pathToExpr = new TreePath(state.getPath(), expr);
   return nullnessAnalysis.getNullness(pathToExpr, state.context);
 }