diff options
Diffstat (limited to 'errorprone')
3 files changed, 205 insertions, 0 deletions
diff --git a/errorprone/Android.bp b/errorprone/Android.bp new file mode 100644 index 000000000000..016b85510a94 --- /dev/null +++ b/errorprone/Android.bp @@ -0,0 +1,25 @@ + +java_plugin { + name: "error_prone_android_framework", + + static_libs: [ + "error_prone_android_framework_lib", + ], +} + +java_library_host { + name: "error_prone_android_framework_lib", + + srcs: ["java/**/*.java"], + + static_libs: [ + "//external/error_prone:error_prone_core", + "//external/dagger2:dagger2-auto-service", + ], + + plugins: [ + "//external/dagger2:dagger2-auto-service", + ], + + javacflags: ["-verbose"], +} diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java new file mode 100644 index 000000000000..1ce816c34990 --- /dev/null +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.errorprone.bugpatterns.android; + +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Matchers.allOf; +import static com.google.errorprone.matchers.Matchers.anyOf; +import static com.google.errorprone.matchers.Matchers.anything; +import static com.google.errorprone.matchers.Matchers.kindIs; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.BinaryTreeMatcher; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.FieldMatchers; +import com.google.errorprone.matchers.Matcher; +import com.sun.source.tree.BinaryTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.Tree.Kind; + +@AutoService(BugChecker.class) +@BugPattern( + name = "AndroidFrameworkTargetSdk", + summary = "Verifies that all target SDK comparisons are sane", + severity = WARNING) +public final class TargetSdkChecker extends BugChecker implements BinaryTreeMatcher { + private static final Matcher<ExpressionTree> VERSION_CODE = FieldMatchers + .anyFieldInClass("android.os.Build.VERSION_CODES"); + + private static final Matcher<BinaryTree> INVALID_OLD_BEHAVIOR = anyOf( + allOf(kindIs(Kind.LESS_THAN_EQUAL), binaryTreeExact(anything(), VERSION_CODE)), + allOf(kindIs(Kind.GREATER_THAN_EQUAL), binaryTreeExact(VERSION_CODE, anything()))); + + private static final Matcher<BinaryTree> INVALID_NEW_BEHAVIOR = anyOf( + allOf(kindIs(Kind.GREATER_THAN), binaryTreeExact(anything(), VERSION_CODE)), + allOf(kindIs(Kind.LESS_THAN), binaryTreeExact(VERSION_CODE, anything()))); + + @Override + public Description matchBinary(BinaryTree tree, VisitorState state) { + if (INVALID_OLD_BEHAVIOR.matches(tree, state)) { + return buildDescription(tree) + .setMessage("Legacy behaviors must be written in style " + + "'targetSdk < Build.VERSION_CODES.Z'") + .build(); + } + if (INVALID_NEW_BEHAVIOR.matches(tree, state)) { + return buildDescription(tree) + .setMessage("Modern behaviors must be written in style " + + "'targetSdk >= Build.VERSION_CODES.Z'") + .build(); + } + return Description.NO_MATCH; + } + + private static Matcher<BinaryTree> binaryTreeExact(Matcher<ExpressionTree> left, + Matcher<ExpressionTree> right) { + return new Matcher<BinaryTree>() { + @Override + public boolean matches(BinaryTree tree, VisitorState state) { + return left.matches(tree.getLeftOperand(), state) + && right.matches(tree.getRightOperand(), state); + } + }; + } +} diff --git a/errorprone/java/com/google/errorprone/matchers/FieldMatchers.java b/errorprone/java/com/google/errorprone/matchers/FieldMatchers.java new file mode 100644 index 000000000000..46f0fb2e534c --- /dev/null +++ b/errorprone/java/com/google/errorprone/matchers/FieldMatchers.java @@ -0,0 +1,99 @@ +/* + * Copyright 2018 The Error Prone Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.errorprone.matchers; + +import com.google.errorprone.VisitorState; +import com.google.errorprone.util.ASTHelpers; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.ImportTree; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import javax.annotation.Nullable; + +// TODO(glorioso): this likely wants to be a fluent interface like MethodMatchers. +// Ex: [staticField()|instanceField()] +// .[onClass(String)|onAnyClass|onClassMatching] +// .[named(String)|withAnyName|withNameMatching] +/** Static utility methods for creating {@link Matcher}s for detecting references to fields. */ +public final class FieldMatchers { + private FieldMatchers() {} + + public static Matcher<ExpressionTree> anyFieldInClass(String className) { + return new FieldReferenceMatcher() { + @Override + boolean classIsAppropriate(ClassSymbol classSymbol) { + return classSymbol.getQualifiedName().contentEquals(className); + } + + @Override + boolean fieldSymbolIsAppropriate(Symbol symbol) { + return true; + } + }; + } + + public static Matcher<ExpressionTree> staticField(String className, String fieldName) { + return new FieldReferenceMatcher() { + @Override + boolean classIsAppropriate(ClassSymbol classSymbol) { + return classSymbol.getQualifiedName().contentEquals(className); + } + + @Override + boolean fieldSymbolIsAppropriate(Symbol symbol) { + return symbol.isStatic() && symbol.getSimpleName().contentEquals(fieldName); + } + }; + } + + public static Matcher<ExpressionTree> instanceField(String className, String fieldName) { + return new FieldReferenceMatcher() { + @Override + boolean classIsAppropriate(ClassSymbol classSymbol) { + return classSymbol.getQualifiedName().contentEquals(className); + } + + @Override + boolean fieldSymbolIsAppropriate(Symbol symbol) { + return !symbol.isStatic() && symbol.getSimpleName().contentEquals(fieldName); + } + }; + } + + private abstract static class FieldReferenceMatcher implements Matcher<ExpressionTree> { + @Override + public boolean matches(ExpressionTree expressionTree, VisitorState state) { + return isSymbolFieldInAppropriateClass(ASTHelpers.getSymbol(expressionTree)) + // Don't match if this is part of a static import tree, since they will get the finding + // on any usage of the field in their source. + && ASTHelpers.findEnclosingNode(state.getPath(), ImportTree.class) == null; + } + + private boolean isSymbolFieldInAppropriateClass(@Nullable Symbol symbol) { + if (symbol == null) { + return false; + } + return symbol.getKind().isField() + && fieldSymbolIsAppropriate(symbol) + && classIsAppropriate(symbol.owner.enclClass()); + } + + abstract boolean fieldSymbolIsAppropriate(Symbol symbol); + + abstract boolean classIsAppropriate(ClassSymbol classSymbol); + } +} |