summaryrefslogtreecommitdiff
path: root/errorprone
diff options
context:
space:
mode:
Diffstat (limited to 'errorprone')
-rw-r--r--errorprone/Android.bp25
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java81
-rw-r--r--errorprone/java/com/google/errorprone/matchers/FieldMatchers.java99
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);
+ }
+}