/** @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); } }
/** 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); } }
/** 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); } }
/** @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; } }
/** @author cushon@google.com (Liam Miller-Cushon) */ @BugPattern( name = "OptionalEquality", summary = "Comparison using reference equality instead of value equality", category = GUAVA, severity = ERROR) public class OptionalEquality extends AbstractReferenceEquality { private static final ImmutableSet<String> OPTIONAL_CLASSES = ImmutableSet.of(com.google.common.base.Optional.class.getName(), "java.util.Optional"); @Override protected boolean matchArgument(ExpressionTree tree, VisitorState state) { Type type = ASTHelpers.getType(tree); for (String className : OPTIONAL_CLASSES) { if (ASTHelpers.isSameType(type, state.getTypeFromString(className), state)) { return true; } } return false; } }
/** * Warns that users should not have an array as a key to a Set or Map * * @author siyuanl@google.com (Siyuan Liu) * @author eleanorh@google.com (Eleanor Harris) */ @BugPattern( name = "ArrayAsKeyOfSetOrMap", summary = "Arrays do not override equals() or hashCode, so comparisons will be done on" + " reference equality only. If neither deduplication nor lookup are needed, " + "consider using a List instead. Otherwise, use IdentityHashMap/Set, " + "a Map from a library that handles object arrays, or an Iterable/List of pairs.", severity = WARNING) public class ArrayAsKeyOfSetOrMap extends AbstractAsKeyOfSetOrMap { @Override protected boolean isBadType(Type type, VisitorState state) { return type instanceof Type.ArrayType; } }
/** @author sgoldfeder@google.com (Steven Goldfeder) */ @BugPattern( name = "GuiceInjectOnFinalField", summary = "Although Guice allows injecting final fields, doing so is disallowed because the injected " + "value may not be visible to other threads.", category = GUICE, severity = ERROR, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class InjectOnFinalField extends BugChecker implements VariableTreeMatcher { private static final Matcher<VariableTree> FINAL_FIELD_WITH_GUICE_INJECT = allOf(isField(), hasModifier(FINAL), hasAnnotation(GUICE_INJECT_ANNOTATION)); @Override public Description matchVariable(VariableTree tree, VisitorState state) { if (FINAL_FIELD_WITH_GUICE_INJECT.matches(tree, state)) { return describeMatch(tree, SuggestedFixes.removeModifiers(tree, state, FINAL)); } return Description.NO_MATCH; } }
/** @author sebastian.h.monte@gmail.com (Sebastian Monte) */ @BugPattern( name = "AssertFalse", summary = "Assertions may be disabled at runtime and do not guarantee that execution will " + "halt here; consider throwing an exception instead", category = JDK, severity = WARNING, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class AssertFalse extends BugChecker implements AssertTreeMatcher { private static final Matcher<AssertTree> ASSERT_FALSE_MATCHER = assertionWithCondition(booleanLiteral(false)); @Override public Description matchAssert(AssertTree tree, VisitorState state) { if (!ASSERT_FALSE_MATCHER.matches(tree, state)) { return Description.NO_MATCH; } return describeMatch(tree, SuggestedFix.replace(tree, "throw new AssertionError()")); } }
/** @author cushon@google.com (Liam Miller-Cushon) */ @BugPattern( name = "DeadThread", summary = "Thread created but not started", severity = ERROR, providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION) public class DeadThread extends BugChecker implements NewClassTreeMatcher { private static final Matcher<ExpressionTree> NEW_THREAD = constructor().forClass("java.lang.Thread"); @Override public Description matchNewClass(NewClassTree tree, VisitorState state) { if (!NEW_THREAD.matches(tree, state)) { return NO_MATCH; } if (state.getPath().getParentPath().getLeaf().getKind() != Kind.EXPRESSION_STATEMENT) { return NO_MATCH; } return describeMatch(tree, SuggestedFix.postfixWith(tree, ".start()")); } }
@AutoService(BugChecker.class) @BugPattern(name = "AlwaysPasses", category = BugPattern.Category.JDK, summary = "A placeholder rule that never matches.", severity = BugPattern.SeverityLevel.ERROR, suppressionAnnotations = {}, linkType = BugPattern.LinkType.NONE) public class AlwaysPasses extends BugChecker implements BugChecker.CompilationUnitTreeMatcher { @Override public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) { return Description.NO_MATCH; } }
@BugPattern( name = "DeprecatedMethods", providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION, summary = "", severity = WARNING) private static class DeprecatedMethodsCheckForTest extends DeprecatedMethodsCheck { @Override String shadowName(String className) { return className.replaceAll("org\\.robolectric\\..*Shadow", "xxx.XShadow"); } @Override String shortShadowName(String className) { return className.replaceAll("Shadow", "XShadow"); } } }