aboutsummaryrefslogtreecommitdiffstats
path: root/lint/cli
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-12-11 10:24:43 -0800
committerTor Norbye <tnorbye@google.com>2012-12-11 10:28:10 -0800
commit6c0eb1ff22473910087e0558110a6785baf65b51 (patch)
tree3e40be417af3ce7c917103387a7f3e805276b6f3 /lint/cli
parentf5129f903171de8aaf70608a9e7cfeef03fa5eec (diff)
downloadsdk-6c0eb1ff22473910087e0558110a6785baf65b51.zip
sdk-6c0eb1ff22473910087e0558110a6785baf65b51.tar.gz
sdk-6c0eb1ff22473910087e0558110a6785baf65b51.tar.bz2
Add lint recycle detector
This lint check looks for missing recycle() calls on resources such as TypedArrays, MotionEvents, and Messages. 41140 New Lint Check: Check Recycling VelocityTracker 41138 New Lint Check: Check Recycling Message 41137 New Lint Check: Check Recycling MotionEvent 41136 New Lint Check: Check Recycling TypedArray In addition, it also flags cases where a method is called on a resource after the resource has been recycled. Change-Id: Ia06a1779519971d5459f1cd98914a6aededc4b83
Diffstat (limited to 'lint/cli')
-rw-r--r--lint/cli/.classpath10
-rw-r--r--lint/cli/src/test/.classpath8
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/RecycleDetectorTest.java76
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.databin0 -> 4321 bytes
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt159
5 files changed, 244 insertions, 9 deletions
diff --git a/lint/cli/.classpath b/lint/cli/.classpath
index ade4f41..3278842 100644
--- a/lint/cli/.classpath
+++ b/lint/cli/.classpath
@@ -5,11 +5,11 @@
<classpathentry combineaccessrules="false" kind="src" path="/common"/>
<classpathentry combineaccessrules="false" kind="src" path="/lint-api"/>
<classpathentry combineaccessrules="false" kind="src" path="/lint-checks"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-analysis-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-analysis-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src-4.0.zip"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/lint/cli/src/test/.classpath b/lint/cli/src/test/.classpath
index 178cd8c..e79b65f 100644
--- a/lint/cli/src/test/.classpath
+++ b/lint/cli/src/test/.classpath
@@ -7,10 +7,10 @@
<classpathentry combineaccessrules="false" kind="src" path="/lint-api"/>
<classpathentry combineaccessrules="false" kind="src" path="/lint-checks"/>
<classpathentry combineaccessrules="false" kind="src" path="/lint-cli"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src.zip"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-tree-4.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/guava-tools/guava-13.0.1.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/guava-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/lombok-ast/lombok-ast-0.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/lombok-ast/src-4.0.zip"/>
<classpathentry combineaccessrules="false" kind="src" path="/layoutlib_api"/>
<classpathentry combineaccessrules="false" kind="src" path="/common"/>
<classpathentry combineaccessrules="false" kind="src" path="/testutils"/>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RecycleDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/RecycleDetectorTest.java
new file mode 100644
index 0000000..ebef046
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/RecycleDetectorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 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.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class RecycleDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new RecycleDetector();
+ }
+
+ public void test() throws Exception {
+ assertEquals(
+ "src/test/pkg/RecycleTest.java:56: Warning: This TypedArray should be recycled after use with #recycle() [Recycle]\n" +
+ " final TypedArray a = getContext().obtainStyledAttributes(attrs,\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:63: Warning: This TypedArray should be recycled after use with #recycle() [Recycle]\n" +
+ " final TypedArray a = getContext().obtainStyledAttributes(new int[0]);\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:79: Warning: This VelocityTracker should be recycled after use with #recycle() [Recycle]\n" +
+ " VelocityTracker tracker = VelocityTracker.obtain();\n" +
+ " ~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:85: Warning: This Message should be recycled after use with #recycle() [Recycle]\n" +
+ " Message message1 = getHandler().obtainMessage();\n" +
+ " ~~~~~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:86: Warning: This Message should be recycled after use with #recycle() [Recycle]\n" +
+ " Message message2 = Message.obtain();\n" +
+ " ~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:92: Warning: This MotionEvent should be recycled after use with #recycle() [Recycle]\n" +
+ " MotionEvent event1 = MotionEvent.obtain(null);\n" +
+ " ~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:93: Warning: This MotionEvent should be recycled after use with #recycle() [Recycle]\n" +
+ " MotionEvent event2 = MotionEvent.obtainNoHistory(null);\n" +
+ " ~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:98: Warning: This MotionEvent should be recycled after use with #recycle() [Recycle]\n" +
+ " MotionEvent event2 = MotionEvent.obtainNoHistory(null); // Not recycled\n" +
+ " ~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:103: Warning: This MotionEvent should be recycled after use with #recycle() [Recycle]\n" +
+ " MotionEvent event1 = MotionEvent.obtain(null); // Not recycled\n" +
+ " ~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:113: Warning: This MotionEvent has already been recycled [Recycle]\n" +
+ " int contents2 = event1.describeContents(); // BAD, after recycle\n" +
+ " ~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:117: Warning: This TypedArray has already been recycled [Recycle]\n" +
+ " example = a.getString(R.styleable.MyView_exampleString); // BAD, after recycle\n" +
+ " ~~~~~~~~~\n" +
+ "src/test/pkg/RecycleTest.java:129: Warning: This Parcel should be recycled after use with #recycle() [Recycle]\n" +
+ " Parcel myparcel = Parcel.obtain();\n" +
+ " ~~~~~~\n" +
+ "0 errors, 12 warnings\n",
+
+ lintProject(
+ "apicheck/classpath=>.classpath",
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "project.properties1=>project.properties",
+ "bytecode/RecycleTest.java.txt=>src/test/pkg/RecycleTest.java",
+ "bytecode/RecycleTest.class.data=>bin/classes/test/pkg/RecycleTest.class"
+ ));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
new file mode 100644
index 0000000..3bdc829
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
new file mode 100644
index 0000000..2a026f2
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
@@ -0,0 +1,159 @@
+package test.pkg;
+
+import com.unit.test.R;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Message;
+import android.os.Parcel;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+
+@SuppressWarnings("unused")
+public class RecycleTest extends View {
+ // ---- Check recycling TypedArrays ----
+
+ public RecycleTest(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void ok1(AttributeSet attrs, int defStyle) {
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.MyView, defStyle, 0);
+ String example = a.getString(R.styleable.MyView_exampleString);
+ a.recycle();
+ }
+
+ public void ok2(AttributeSet attrs, int defStyle) {
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.MyView, defStyle, 0);
+ String example = a.getString(R.styleable.MyView_exampleString);
+ // If there's complicated logic, don't flag
+ if (something()) {
+ a.recycle();
+ }
+ }
+
+ public TypedArray ok3(AttributeSet attrs, int defStyle) {
+ // Value passes out of method: don't flag, caller might be recycling
+ return getContext().obtainStyledAttributes(attrs, R.styleable.MyView,
+ defStyle, 0);
+ }
+
+ private TypedArray myref;
+
+ public void ok4(AttributeSet attrs, int defStyle) {
+ // Value stored in a field: might be recycled later
+ TypedArray ref = getContext().obtainStyledAttributes(attrs,
+ R.styleable.MyView, defStyle, 0);
+ myref = ref;
+ }
+
+ public void wrong1(AttributeSet attrs, int defStyle) {
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.MyView, defStyle, 0);
+ String example = a.getString(R.styleable.MyView_exampleString);
+ // a.recycle();
+ }
+
+ public void wrong2(AttributeSet attrs, int defStyle) {
+ final TypedArray a = getContext().obtainStyledAttributes(new int[0]);
+ // a.recycle();
+ }
+
+ public void unknown(AttributeSet attrs, int defStyle) {
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.MyView, defStyle, 0);
+ // We don't know what this method is (usually it will be in a different
+ // class)
+ // so don't flag it; it might recycle
+ handle(a);
+ }
+
+ // ---- Check recycling VelocityTracker ----
+
+ public void tracker() {
+ VelocityTracker tracker = VelocityTracker.obtain();
+ }
+
+ // ---- Check recycling Message ----
+
+ public void message() {
+ Message message1 = getHandler().obtainMessage();
+ Message message2 = Message.obtain();
+ }
+
+ // ---- Check recycling MotionEvent ----
+
+ public void motionEvent() {
+ MotionEvent event1 = MotionEvent.obtain(null);
+ MotionEvent event2 = MotionEvent.obtainNoHistory(null);
+ }
+
+ public void motionEvent2() {
+ MotionEvent event1 = MotionEvent.obtain(null); // OK
+ MotionEvent event2 = MotionEvent.obtainNoHistory(null); // Not recycled
+ event1.recycle();
+ }
+
+ public void motionEvent3() {
+ MotionEvent event1 = MotionEvent.obtain(null); // Not recycled
+ MotionEvent event2 = MotionEvent.obtain(event1);
+ event2.recycle();
+ }
+
+ // ---- Using recycled objects ----
+
+ public void recycled() {
+ MotionEvent event1 = MotionEvent.obtain(null); // Not recycled
+ event1.recycle();
+ int contents2 = event1.describeContents(); // BAD, after recycle
+ final TypedArray a = getContext().obtainStyledAttributes(new int[0]);
+ String example = a.getString(R.styleable.MyView_exampleString); // OK
+ a.recycle();
+ example = a.getString(R.styleable.MyView_exampleString); // BAD, after recycle
+ }
+
+ // ---- Check recycling Parcel ----
+
+ public void parcelOk() {
+ Parcel myparcel = Parcel.obtain();
+ myparcel.createBinderArray();
+ myparcel.recycle();
+ }
+
+ public void parcelMissing() {
+ Parcel myparcel = Parcel.obtain();
+ myparcel.createBinderArray();
+ }
+
+
+ // ---- Check suppress ----
+
+ @SuppressLint("Recycle")
+ public void recycledSuppress() {
+ MotionEvent event1 = MotionEvent.obtain(null); // Not recycled
+ event1.recycle();
+ int contents2 = event1.describeContents(); // BAD, after recycle
+ final TypedArray a = getContext().obtainStyledAttributes(new int[0]);
+ String example = a.getString(R.styleable.MyView_exampleString); // OK
+ }
+
+ // ---- Stubs ----
+
+ static void handle(TypedArray a) {
+ // Unknown method
+ }
+
+ protected boolean something() {
+ return true;
+ }
+
+ public android.content.res.TypedArray obtainStyledAttributes(
+ AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
+ return null;
+ }
+}