private static boolean hasAttribute(Symbol symbol, String name, VisitorState state) { Symbol annotation = state.getSymbolFromString(name); // If we can't look up the annotation in the current VisitorState, then presumably it couldn't // be present on a Symbol we're inspecting. return annotation != null && symbol.attribute(annotation) != null; }
private static Type predicateType(Type type, VisitorState state) { Symbol predicate = state.getSymbolFromString(java.util.function.Predicate.class.getName()); if (predicate == null) { return null; } Type asPredicate = state.getTypes().asSuper(type, predicate); if (asPredicate == null) { return null; } return getOnlyElement(asPredicate.getTypeArguments(), null); }
private AnnotationTree findAnnotation( MethodTree methodTree, VisitorState state, String annotationName) { AnnotationTree annotationNode = null; for (AnnotationTree annotation : methodTree.getModifiers().getAnnotations()) { if (ASTHelpers.getSymbol(annotation).equals(state.getSymbolFromString(annotationName))) { annotationNode = annotation; } } return annotationNode; }
@Nullable private Type getTypeFromStringInternal(String typeStr) { validateTypeStr(typeStr); Type primitiveOrVoidType = getPrimitiveOrVoidType(typeStr); if (primitiveOrVoidType != null) { return primitiveOrVoidType; } // Fast path if the type's symbol is available. ClassSymbol classSymbol = (ClassSymbol) getSymbolFromString(typeStr); if (classSymbol != null) { return classSymbol.asType(); } return null; }
/** * Returns if the variable has a {@code @Mock} annotation that specifies an answer that does not * handle generics. */ static boolean answerHandlesGenerics(VarSymbol varSym, VisitorState state) { Compound attribute = varSym.attribute(state.getSymbolFromString(MOCK_ANNOTATION)); String answer = null; for (Entry<MethodSymbol, Attribute> e : attribute.getElementValues().entrySet()) { if (e.getKey().getSimpleName().contentEquals("answer")) { answer = e.getValue().getValue().toString(); break; } } return !BAD_ANSWER_STRATEGIES.contains(answer); } }
@Override Type extractSourceType(MethodInvocationTree tree, VisitorState state) { return extractTypeArgAsMemberOfSupertype( ASTHelpers.getType(Iterables.get(tree.getArguments(), methodArgIndex)), state.getSymbolFromString(methodArgTypeName), methodArgTypeArgIndex, state.getTypes()); }
/** Returns true if the given method symbol has a {@code @Test(expected=...)} annotation. */ private static boolean isExpectedExceptionTest(MethodSymbol sym, VisitorState state) { Compound attribute = sym.attribute(state.getSymbolFromString(JUnitMatchers.JUNIT4_TEST_ANNOTATION)); if (attribute == null) { return false; } return attribute.member(state.getName("expected")) != null; }
@Override Type extractTargetType(MethodInvocationTree tree, VisitorState state) { return extractTypeArgAsMemberOfSupertype( ASTHelpers.getReceiverType(tree), state.getSymbolFromString(typeName), typeArgIndex, state.getTypes()); } }
@Override Type extractTargetType(MethodInvocationTree tree, VisitorState state) { return extractTypeArgAsMemberOfSupertype( ASTHelpers.getReceiverType(tree), state.getSymbolFromString(receiverTypeName), receiverTypeArgIndex, state.getTypes()); }
/** * Returns true if the lock expression corresponds to a {@code * java.util.concurrent.locks.ReadWriteLock}. */ private static boolean isRWLock(GuardedByExpression guard, VisitorState state) { Type guardType = guard.type(); if (guardType == null) { return false; } Symbol rwLockSymbol = state.getSymbolFromString(JUC_READ_WRITE_LOCK); if (rwLockSymbol == null) { return false; } return state.getTypes().isSubtype(guardType, rwLockSymbol.type); }
private static boolean isInherited(VisitorState state, String annotationName) { Symbol annotationSym = state.getSymbolFromString(annotationName); if (annotationSym == null) { return false; } try { annotationSym.complete(); } catch (CompletionFailure e) { // @Inherited won't work if the annotation isn't on the classpath, but we can still check // if it's present directly } Symbol inheritedSym = state.getSymtab().inheritedType.tsym; return annotationSym.attribute(inheritedSym) != null; }
/** * @param symStr the string representation of a symbol * @return the Symbol object, or null if it cannot be found */ // TODO(cushon): deal with binary compat issues and return ClassSymbol @Nullable public Symbol getSymbolFromString(String symStr) { symStr = inferBinaryName(symStr); Name name = getName(symStr); Modules modules = Modules.instance(context); boolean modular = modules.getDefaultModule() != getSymtab().noModule; if (!modular) { return getSymbolFromString(getSymtab().noModule, name); } for (ModuleSymbol msym : Modules.instance(context).allModules()) { ClassSymbol result = getSymbolFromString(msym, name); if (result != null) { // TODO(cushon): the path where we iterate over all modules is probably slow. // Try to learn some lessons from JDK-8189747, and consider disallowing this case and // requiring users to call the getSymbolFromString(ModuleSymbol, Name) overload instead. return result; } } return null; }
@Override public boolean matches(Tree tree, VisitorState state) { Symbol sym = state.getSymbolFromString(clazz); if (sym == null) { return false; } Type type = ASTHelpers.getType(tree); if (!ASTHelpers.isSubtype(type, sym.type, state)) { return false; } Types types = state.getTypes(); Type superType = types.asSuper(type, sym); if (superType == null) { return false; } List<Type> typeArguments = superType.getTypeArguments(); if (typeArguments.isEmpty()) { return false; } return ASTHelpers.isSameType( typeArguments.get(typeArgumentIndex), state.getTypeFromString(URL_CLASS), state); } }
private static Optional<Fix> linkedListFix(Tree tree, VisitorState state) { Type type = getTargetType(state); if (type == null) { return Optional.empty(); } Types types = state.getTypes(); for (String replacement : ImmutableList.of("java.util.ArrayList", "java.util.ArrayDeque")) { Symbol sym = state.getSymbolFromString(replacement); if (sym == null) { continue; } if (types.isAssignable(types.erasure(sym.asType()), types.erasure(type))) { SuggestedFix.Builder fix = SuggestedFix.builder(); while (tree instanceof ParameterizedTypeTree) { tree = ((ParameterizedTypeTree) tree).getType(); } fix.replace(tree, SuggestedFixes.qualifyType(state, fix, sym)); return Optional.of(fix.build()); } } return Optional.empty(); }
@Override public Description matchCompilationUnit(CompilationUnitTree tree, final VisitorState state) { Symbol mockitoSym = state.getSymbolFromString(MOCKITO_CLASS); if (mockitoSym == null) { // fast path if mockito isn't being used return Description.NO_MATCH; } // collect variable symbols for standard Answer constants that don't support generics final Set<Symbol> badAnswers = new LinkedHashSet<>(); for (Symbol member : mockitoSym.members().getSymbols(LookupKind.NON_RECURSIVE)) { if (member.getKind() != ElementKind.FIELD) { continue; } if (BAD_ANSWER_STRATEGIES.contains(member.getSimpleName().toString())) { badAnswers.add(member); } } // collect mocks that are initialized in this compilation unit using a bad answer strategy final Set<VarSymbol> mockVariables = MockInitializationScanner.scan(state, badAnswers); // check for when(...) calls on mocks using a bad answer strategy new WhenNeedsCastScanner(mockVariables, state).scan(state.getPath(), null); // errors are reported in WhenNeedsCastScanner return Description.NO_MATCH; }
@Override public boolean matches(ExpressionTree t, VisitorState state) { Type type = ((JCTree) t).type; // Expect a class type. if (!(type instanceof ClassType)) { return false; } // Expect one type argument, the type of the JUnit class runner to use. com.sun.tools.javac.util.List<Type> typeArgs = ((ClassType) type).getTypeArguments(); if (typeArgs.size() != 1) { return false; } Type runnerType = typeArgs.get(0); for (String testRunner : TEST_RUNNERS) { Symbol parent = state.getSymbolFromString(testRunner); if (parent == null) { continue; } if (runnerType.tsym.isSubClass(parent, state.getTypes())) { return true; } } return false; } };
@Override public Description matchMethodInvocation( MethodInvocationTree methodInvocationTree, VisitorState visitorState) { if (!TO_ARRAY_MATCHER.matches(methodInvocationTree, visitorState)) { return NO_MATCH; } Types types = visitorState.getTypes(); Type variableType = types.elemtype(getType(getOnlyElement(methodInvocationTree.getArguments()))); if (variableType == null) { return NO_MATCH; } Type collectionType = types.asSuper( ASTHelpers.getReceiverType(methodInvocationTree), visitorState.getSymbolFromString("java.util.Collection")); List<Type> typeArguments = collectionType.getTypeArguments(); if (!typeArguments.isEmpty() && !types.isCastable( types.erasure(variableType), types.erasure(getOnlyElement(typeArguments)))) { return describeMatch(methodInvocationTree); } return NO_MATCH; } }
/** Ignore some common ThreadLocal type arguments that are fine to have per-instance copies of. */ private boolean wellKnownTypeArgument(NewClassTree tree, VisitorState state) { Type type = getType(tree); if (type == null) { return false; } type = state.getTypes().asSuper(type, state.getSymbolFromString("java.lang.ThreadLocal")); if (type == null) { return false; } if (type.getTypeArguments().isEmpty()) { return false; } Type argType = getOnlyElement(type.getTypeArguments()); if (WELL_KNOWN_TYPES.contains(argType.asElement().getQualifiedName().toString())) { return true; } if (isSubtype(argType, state.getTypeFromString("java.text.DateFormat"), state)) { return true; } return false; } }
@Override public Description matchClass(ClassTree tree, VisitorState state) { if (!PARCELABLE_MATCHER.matches(tree, state)) { return Description.NO_MATCH; } Symbol parcelableCreatorSymbol = state.getSymbolFromString("android.os.Parcelable$Creator"); if (parcelableCreatorSymbol == null) { return Description.NO_MATCH; } ClassType classType = ASTHelpers.getType(tree); for (Tree member : tree.getMembers()) { if (member.getKind() != Kind.VARIABLE) { continue; } VariableTree variableTree = (VariableTree) member; if (PARCELABLE_CREATOR_MATCHER.matches(variableTree, state)) { if (isVariableClassCreator(variableTree, state, classType, parcelableCreatorSymbol)) { return Description.NO_MATCH; } } } return describeMatch(tree); }
private Description describe( ClassTree classTree, VisitorState state, @Nullable Retention retention) { if (retention == null) { return describeMatch( classTree, SuggestedFix.builder() .addImport("java.lang.annotation.Retention") .addStaticImport("java.lang.annotation.RetentionPolicy.RUNTIME") .prefixWith(classTree, "@Retention(RUNTIME)\n") .build()); } AnnotationTree retentionNode = null; for (AnnotationTree annotation : classTree.getModifiers().getAnnotations()) { if (ASTHelpers.getSymbol(annotation) .equals(state.getSymbolFromString(RETENTION_ANNOTATION))) { retentionNode = annotation; } } return describeMatch( retentionNode, SuggestedFix.builder() .addImport("java.lang.annotation.Retention") .addStaticImport("java.lang.annotation.RetentionPolicy.RUNTIME") .replace(retentionNode, "@Retention(RUNTIME)") .build()); } }