/** Checks that {@link AsyncFunction} implementations do not directly {@code return null}. */ @BugPattern( name = "AsyncFunctionReturnsNull", summary = "AsyncFunction should not return a null Future, only a Future whose result is null.", category = GUAVA, severity = ERROR, generateExamplesFromTestCases = false) public final class AsyncFunctionReturnsNull extends AbstractAsyncTypeReturnsNull { public AsyncFunctionReturnsNull() { super(AsyncFunction.class); } }
public static BugPatternInstance fromElement(Element element) { BugPatternInstance instance = new BugPatternInstance(); instance.className = element.toString(); BugPattern annotation = element.getAnnotation(BugPattern.class); instance.name = annotation.name(); instance.altNames = annotation.altNames(); instance.tags = annotation.tags(); instance.severity = annotation.severity(); instance.summary = annotation.summary(); instance.explanation = annotation.explanation(); instance.documentSuppression = annotation.documentSuppression(); instance.providesFix = annotation.providesFix(); Map<String, Object> keyValues = getAnnotation(element, BugPattern.class.getName()); Object suppression = keyValues.get("suppressionAnnotations"); if (suppression == null) { instance.suppressionAnnotations = new String[] {SuppressWarnings.class.getName()}; } else { Preconditions.checkState(suppression instanceof List); @SuppressWarnings("unchecked") // Always List<? extends AnnotationValue>, see above. List<? extends AnnotationValue> resultList = (List<? extends AnnotationValue>) suppression; instance.suppressionAnnotations = resultList.stream().map(AnnotationValue::toString).toArray(String[]::new); } instance.generateExamplesFromTestCases = !keyValues.containsKey("generateExamplesFromTestCases") || (boolean) keyValues.get("generateExamplesFromTestCases"); return instance; }
private BugCheckerInfo(Class<? extends BugChecker> checker, BugPattern pattern) { this( checker, pattern.name(), ImmutableSet.<String>builder().add(pattern.name()).add(pattern.altNames()).build(), pattern.summary(), pattern.severity(), createLinkUrl(pattern), Stream.of(pattern.suppressionAnnotations()).anyMatch(a -> isSuppressWarnings(a)), Stream.of(pattern.suppressionAnnotations()) .filter(a -> !isSuppressWarnings(a)) .collect(toImmutableSet()), ImmutableSet.copyOf(pattern.tags()), pattern.disableable()); }
@Nullable private static String createLinkUrl(BugPattern pattern) { switch (pattern.linkType()) { case AUTOGENERATED: return String.format(URL_FORMAT, pattern.name()); case CUSTOM: // annotation.link() must be provided. if (pattern.link().isEmpty()) { throw new IllegalStateException( "If linkType element of @BugPattern is CUSTOM, " + "a link element must also be provided."); } return pattern.link(); case NONE: return null; } throw new AssertionError( "Unexpected value for linkType element of @BugPattern: " + pattern.linkType()); }
/** * Returns a new {@link CompilationTestHelper}. * * @param checker the {@link BugChecker} to test * @param clazz the class to use to locate file resources */ @CheckReturnValue public static CompilationTestHelper newInstance( Class<? extends BugChecker> checker, Class<?> clazz) { ScannerSupplier scannerSupplier = ScannerSupplier.fromBugCheckerClasses(checker); String checkName = checker.getAnnotation(BugPattern.class).name(); return new CompilationTestHelper(scannerSupplier, checkName, clazz); }
private BugCheckerInfo(Class<? extends BugChecker> checker, BugPattern pattern) { this( checker, pattern.name(), ImmutableSet.<String>builder().add(pattern.name()).add(pattern.altNames()).build(), pattern.summary(), pattern.severity(), createLinkUrl(pattern), Stream.of(pattern.suppressionAnnotations()).anyMatch(a -> isSuppressWarnings(a)), Stream.of(pattern.suppressionAnnotations()) .filter(a -> !isSuppressWarnings(a)) .collect(toImmutableSet()), ImmutableSet.copyOf(pattern.tags()), pattern.disableable()); }
public static void validate(BugPattern pattern) throws ValidationException { if (pattern == null) { throw new ValidationException("No @BugPattern provided"); } // name must not contain spaces if (CharMatcher.whitespace().matchesAnyOf(pattern.name())) { throw new ValidationException("Name must not contain whitespace: " + pattern.name()); } // linkType must be consistent with link element. switch (pattern.linkType()) { case CUSTOM: if (pattern.link().isEmpty()) { throw new ValidationException("Expected a custom link but none was provided"); } break; case AUTOGENERATED: case NONE: if (!pattern.link().isEmpty()) { throw new ValidationException("Expected no custom link but found: " + pattern.link()); } break; } } }
/** Checks that {@link AsyncCallable} implementations do not directly {@code return null}. */ @BugPattern( name = "AsyncCallableReturnsNull", summary = "AsyncCallable should not return a null Future, only a Future whose result is null.", category = GUAVA, severity = ERROR) public final class AsyncCallableReturnsNull extends AbstractAsyncTypeReturnsNull { public AsyncCallableReturnsNull() { super(AsyncCallable.class); } }
private static String createLinkUrl(BugPattern pattern) { switch (pattern.linkType()) { case AUTOGENERATED: return String.format(URL_FORMAT, pattern.name()); case CUSTOM: // annotation.link() must be provided. if (pattern.link().isEmpty()) { throw new IllegalStateException( "If linkType element of @BugPattern is CUSTOM, " + "a link element must also be provided."); } return pattern.link(); case NONE: return null; } throw new AssertionError( "Unexpected value for linkType element of @BugPattern: " + pattern.linkType()); }
/** @author mwacker@google.com (Mike Wacker) */ @BugPattern( name = "JUnitAmbiguousTestClass", summary = "Test class inherits from JUnit 3's TestCase but has JUnit 4 @Test annotations.", category = JUNIT, severity = WARNING) public class JUnitAmbiguousTestClass extends BugChecker implements ClassTreeMatcher { @Override public Description matchClass(ClassTree classTree, VisitorState state) { return isAmbiguousJUnitVersion.matches(classTree, state) ? describeMatch(classTree) : NO_MATCH; } }
/** @author ptoomey@google.com (Patrick Toomey) */ @BugPattern( name = "StringEquality", summary = "String comparison using reference equality instead of value equality", category = JDK, severity = WARNING) public class StringEquality extends AbstractReferenceEquality { @Override protected boolean matchArgument(ExpressionTree tree, VisitorState state) { return ASTHelpers.isSameType(ASTHelpers.getType(tree), state.getSymtab().stringType, state); } }
/** @author glorioso@google.com */ @BugPattern( name = "ClassNamedLikeTypeParameter", summary = "This class's name looks like a Type Parameter.", category = JDK, severity = SUGGESTION, tags = StandardTags.STYLE) public class ClassNamedLikeTypeParameter extends BugChecker implements ClassTreeMatcher { @Override public Description matchClass(ClassTree tree, VisitorState state) { // Here, if a class is named like a Type Parameter, it's a bad thing. return TypeParameterNamingClassification.classify(tree.getSimpleName().toString()).isValidName() ? describeMatch(tree) : Description.NO_MATCH; } }
/** @author cushon@google.com (Liam Miller-Cushon) */ @BugPattern( name = "ThrowNull", category = JDK, summary = "Throwing 'null' always results in a NullPointerException being thrown.", severity = ERROR, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class ThrowNull extends BugChecker implements ThrowTreeMatcher { @Override public Description matchThrow(ThrowTree tree, VisitorState state) { return (tree.getExpression().getKind() == NULL_LITERAL) ? describeMatch( tree, SuggestedFix.replace(tree.getExpression(), "new NullPointerException()")) : NO_MATCH; } }
/** @author avenet@google.com (Arnaud J. Venet) */ @BugPattern( name = "RectIntersectReturnValueIgnored", summary = "Return value of android.graphics.Rect.intersect() must be checked", category = ANDROID, severity = ERROR) public final class RectIntersectReturnValueIgnored extends AbstractReturnValueIgnored { @Override public Matcher<? super ExpressionTree> specializedMatcher() { return instanceMethod().onExactClass("android.graphics.Rect").named("intersect"); } @Override public Description describe(MethodInvocationTree methodInvocationTree, VisitorState state) { return describeMatch(methodInvocationTree); } }
/** @author cushon@google.com (Liam Miller-Cushon) */ @BugPattern( name = "VarTypeName", summary = "`var` should not be used as a type name.", severity = ERROR) public class VarTypeName extends BugChecker implements ClassTreeMatcher, TypeParameterTreeMatcher { @Override public Description matchClass(ClassTree tree, VisitorState state) { return check(tree, tree.getSimpleName()); } @Override public Description matchTypeParameter(TypeParameterTree tree, VisitorState state) { return check(tree, tree.getName()); } private Description check(Tree tree, Name name) { return name.contentEquals("var") ? describeMatch(tree) : NO_MATCH; } }
/** @author glorioso@google.com (Nick Glorioso) */ @BugPattern( name = "ProvidesMethodOutsideOfModule", summary = "@Provides methods need to be declared in a Module to have any effect.", category = GUICE, severity = ERROR, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class ProvidesMethodOutsideOfModule extends BugChecker implements AnnotationTreeMatcher { private static final Matcher<AnnotationTree> PROVIDES_ANNOTATION_ON_METHOD_OUTSIDE_OF_MODULE = allOf(isType(GUICE_PROVIDES_ANNOTATION), not(INSIDE_GUICE_MODULE)); @Override public Description matchAnnotation(AnnotationTree annotation, VisitorState state) { if (PROVIDES_ANNOTATION_ON_METHOD_OUTSIDE_OF_MODULE.matches(annotation, state)) { return describeMatch(annotation, SuggestedFix.delete(annotation)); } return Description.NO_MATCH; } }
/** @author sgoldfeder@google.com (Steven Goldfeder) */ @BugPattern( name = "JavaxInjectOnFinalField", summary = "@javax.inject.Inject cannot be put on a final field.", category = INJECT, severity = ERROR, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class JavaxInjectOnFinalField extends BugChecker implements AnnotationTreeMatcher { @Override public Description matchAnnotation(AnnotationTree annotationTree, VisitorState state) { if (IS_APPLICATION_OF_JAVAX_INJECT.matches(annotationTree, state)) { if (isFinalField(getSymbol(state.getPath().getParentPath().getParentPath().getLeaf()))) { return describeMatch(annotationTree, SuggestedFix.delete(annotationTree)); } } return Description.NO_MATCH; } }
/** * Check for usage of {@code Set<Proto>} or {@code Map<Proto, E>}. * * @author seibelsabrina@google.com (Sabrina Seibel) */ @BugPattern( name = "ProtosAsKeyOfSetOrMap", summary = "Protos should not be used as a key to a map, in a set, or in a contains method on a " + "descendant of a collection. Protos have non deterministic ordering and proto " + "equality is deep, which is a performance issue.", severity = WARNING) public class ProtosAsKeyOfSetOrMap extends AbstractAsKeyOfSetOrMap { @Override protected boolean isBadType(Type type, VisitorState state) { return ASTHelpers.isSubtype( type, state.getTypeFromString("com.google.protobuf.GeneratedMessage"), state); } }
/** @author cushon@google.com (Liam Miller-Cushon) */ @BugPattern( name = "UnnecessaryStaticImport", summary = "Using static imports for types is unnecessary", category = JDK, severity = SUGGESTION, documentSuppression = false, tags = StandardTags.STYLE, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class UnnecessaryStaticImport extends BugChecker implements ImportTreeMatcher { @Override public Description matchImport(ImportTree tree, VisitorState state) { StaticImportInfo importInfo = StaticImports.tryCreate(tree, state); if (importInfo == null || !importInfo.members().isEmpty()) { return Description.NO_MATCH; } return describeMatch(tree, SuggestedFix.replace(tree, importInfo.importStatement())); } }
/** @author sulku@google.com (Marsela Sulku) */ @BugPattern( name = "FuzzyEqualsShouldNotBeUsedInEqualsMethod", summary = "DoubleMath.fuzzyEquals should never be used in an Object.equals() method", category = GUAVA, severity = ERROR) public class FuzzyEqualsShouldNotBeUsedInEqualsMethod extends BugChecker implements MethodInvocationTreeMatcher { private static final Matcher<MethodInvocationTree> CALL_TO_FUZZY_IN_EQUALS = allOf( staticMethod().onClass("com.google.common.math.DoubleMath").named("fuzzyEquals"), enclosingMethod(Matchers.equalsMethodDeclaration())); @Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { if (CALL_TO_FUZZY_IN_EQUALS.matches(tree, state)) { return describeMatch(tree); } return Description.NO_MATCH; } }