summaryrefslogtreecommitdiff
path: root/errorprone
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2020-10-05 16:49:24 -0600
committerJeff Sharkey <jsharkey@android.com>2020-10-05 20:12:33 -0600
commit749789d24047f546a18392f83a9438bc737de490 (patch)
tree9a58d9a28a1f0977d7d32d5974ebda008b7f17cd /errorprone
parent1561df4a9b09a4118c3dfddd80c5691a183a9939 (diff)
Detector to suggest more efficient collections.
Android offers several efficient alternatives to some upstream Collections containers, such as SparseIntArray instead of Map<Integer, Integer>. Bug: 155703208 Test: atest error_prone_android_framework_test Change-Id: I080fd9489fb037391b717901345a905a9753b370
Diffstat (limited to 'errorprone')
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java98
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java96
2 files changed, 194 insertions, 0 deletions
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
new file mode 100644
index 000000000000..c4c1ab6482ee
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
@@ -0,0 +1,98 @@
+/*
+ * 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.isSubtypeOf;
+
+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.NewClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Type;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Android offers several efficient alternatives to some upstream
+ * {@link Collections} containers, such as {@code SparseIntArray} instead of
+ * {@code Map<Integer, Integer>}.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkEfficientCollections",
+ summary = "Verifies efficient collections best-practices",
+ severity = WARNING)
+public final class EfficientCollectionsChecker extends BugChecker implements NewClassTreeMatcher {
+ private static final Matcher<Tree> IS_LIST = isSubtypeOf("java.util.List");
+ private static final Matcher<Tree> IS_MAP = isSubtypeOf("java.util.Map");
+
+ private static final String INTEGER = "java.lang.Integer";
+ private static final String LONG = "java.lang.Long";
+ private static final String BOOLEAN = "java.lang.Boolean";
+
+ @Override
+ public Description matchNewClass(NewClassTree tree, VisitorState state) {
+ final List<Type> types = ASTHelpers.getType(tree).getTypeArguments();
+ if (IS_LIST.matches(tree, state) && types != null && types.size() == 1) {
+ final Type first = types.get(0);
+ if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with IntArray for efficiency")
+ .build();
+ } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with LongArray for efficiency")
+ .build();
+ }
+ } else if (IS_MAP.matches(tree, state) && types != null && types.size() == 2) {
+ final Type first = types.get(0);
+ final Type second = types.get(1);
+ if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+ if (ASTHelpers.isSameType(second, state.getTypeFromString(INTEGER), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseIntArray for efficiency")
+ .build();
+ } else if (ASTHelpers.isSameType(second, state.getTypeFromString(LONG), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseLongArray for efficiency")
+ .build();
+ } else if (ASTHelpers.isSameType(second, state.getTypeFromString(BOOLEAN), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseBooleanArray for efficiency")
+ .build();
+ } else {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with SparseArray for efficiency")
+ .build();
+ }
+ } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) {
+ return buildDescription(tree)
+ .setMessage("Consider replacing with LongSparseArray for efficiency")
+ .build();
+ }
+ }
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java
new file mode 100644
index 000000000000..e128b6ad2cfd
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientCollectionsCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ EfficientCollectionsChecker.class, getClass());
+ }
+
+ @Test
+ public void testMap() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import java.util.HashMap;",
+ "public class Example {",
+ " public void exampleInteger() {",
+ " // BUG: Diagnostic contains:",
+ " HashMap<Integer, Integer> a = new HashMap<>();",
+ " // BUG: Diagnostic contains:",
+ " HashMap<Integer, Long> b = new HashMap<>();",
+ " // BUG: Diagnostic contains:",
+ " HashMap<Integer, Boolean> c = new HashMap<>();",
+ " // BUG: Diagnostic contains:",
+ " HashMap<Integer, String> d = new HashMap<>();",
+ " }",
+ " public void exampleLong() {",
+ " // BUG: Diagnostic contains:",
+ " HashMap<Long, String> res = new HashMap<>();",
+ " }",
+ " public void exampleOther() {",
+ " HashMap<String, String> res = new HashMap<>();",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testList() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import java.util.ArrayList;",
+ "public class Example {",
+ " public void exampleInteger() {",
+ " // BUG: Diagnostic contains:",
+ " ArrayList<Integer> res = new ArrayList<>();",
+ " }",
+ " public void exampleLong() {",
+ " // BUG: Diagnostic contains:",
+ " ArrayList<Long> res = new ArrayList<>();",
+ " }",
+ " public void exampleOther() {",
+ " ArrayList<String> res = new ArrayList<>();",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testErasure() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import java.util.HashMap;",
+ "public class Example {",
+ " public void example() {",
+ " HashMap a = new HashMap();",
+ " }",
+ "}")
+ .doTest();
+ }
+}