diff options
9 files changed, 108 insertions, 5 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java index fcf58f4..fc7e03b 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/JavaVisitor.java @@ -198,6 +198,7 @@ public class JavaVisitor { // with details, location, etc. return; } + context.compilationUnit = compilationUnit; for (VisitingDetector v : mAllDetectors) { v.setContext(context); diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java index ff05270..f4f4efa 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java @@ -259,15 +259,16 @@ public class ClassContext extends Context { } ClassNode curr = mClassNode; while (curr != null) { + ClassNode prev = curr; curr = mDriver.getOuterClassNode(curr); if (curr != null) { - if (mClassNode.outerMethod != null) { + if (prev.outerMethod != null) { @SuppressWarnings("rawtypes") // ASM API List methods = curr.methods; for (Object m : methods) { MethodNode method = (MethodNode) m; - if (method.name.equals(mClassNode.outerMethod) - && method.desc.equals(mClassNode.outerMethodDesc)) { + if (method.name.equals(prev.outerMethod) + && method.desc.equals(prev.outerMethodDesc)) { // Found the outer method for this anonymous class; continue // reporting on it (which will also work its way up the parent // class hierarchy) diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java index 7c8154e..c670952 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java @@ -21,6 +21,7 @@ import static com.android.tools.lint.detector.api.LintConstants.TARGET_API; import com.android.annotations.NonNull; import com.android.resources.ResourceFolderType; +import com.android.tools.lint.client.api.LintDriver; import com.android.tools.lint.detector.api.Category; import com.android.tools.lint.detector.api.ClassContext; import com.android.tools.lint.detector.api.Context; @@ -238,8 +239,7 @@ public class ApiDetector extends ResourceXmlDetector implements Detector.ClassSc return; } - // Workaround for the fact that beforeCheckProject is too early - int classMinSdk = getLocalMinSdk(classNode.invisibleAnnotations); + int classMinSdk = getClassMinSdk(context, classNode); if (classMinSdk == -1) { classMinSdk = getMinSdk(context); } @@ -367,6 +367,53 @@ public class ApiDetector extends ResourceXmlDetector implements Detector.ClassSc } /** + * Return the {@code @TargeTApi} level to use for the given {@code classNode}; + * this will be the {@code @TargetApi} annotation on the class, or any outer + * methods (for anonymous inner classes) or outer classes (for inner classes) + * of the given class. + */ + private int getClassMinSdk(ClassContext context, ClassNode classNode) { + int classMinSdk = getLocalMinSdk(classNode.invisibleAnnotations); + if (classMinSdk != -1) { + return classMinSdk; + } + + LintDriver driver = context.getDriver(); + while (classNode != null) { + ClassNode prev = classNode; + classNode = driver.getOuterClassNode(classNode); + if (classNode != null) { + // TODO: Should this be "curr" instead? + if (prev.outerMethod != null) { + @SuppressWarnings("rawtypes") // ASM API + List methods = classNode.methods; + for (Object m : methods) { + MethodNode method = (MethodNode) m; + if (method.name.equals(prev.outerMethod) + && method.desc.equals(prev.outerMethodDesc)) { + // Found the outer method for this anonymous class; check method + // annotations on it, then continue up the class hierarchy + int methodMinSdk = getLocalMinSdk(method.invisibleAnnotations); + if (methodMinSdk != -1) { + return methodMinSdk; + } + + break; + } + } + } + + classMinSdk = getLocalMinSdk(classNode.invisibleAnnotations); + if (classMinSdk != -1) { + return classMinSdk; + } + } + } + + return -1; + } + + /** * Returns the minimum SDK to use according to the given annotation list, or * -1 if no annotation was found. * diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java index eb35e1d..39a1e4f 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java @@ -271,6 +271,21 @@ public class ApiDetectorTest extends AbstractCheckTest { )); } + public void testTargetAnnotationInner() throws Exception { + assertEquals( + "ApiTargetTest2.java:32: Error: Call requires API level 14 (current min is 3): android.widget.GridLayout#<init>", + + lintProject( + "apicheck/classpath=>.classpath", + "apicheck/minsdk1.xml=>AndroidManifest.xml", + "apicheck/ApiTargetTest2.java.txt=>src/test/pkg/ApiTargetTest2.java", + "apicheck/ApiTargetTest2.class.data=>bin/classes/test/pkg/ApiTargetTest2.class", + "apicheck/ApiTargetTest2$1.class.data=>bin/classes/test/pkg/ApiTargetTest2$1.class", + "apicheck/ApiTargetTest2$1$2.class.data=>bin/classes/test/pkg/ApiTargetTest2$1$2.class", + "apicheck/ApiTargetTest2$1$1.class.data=>bin/classes/test/pkg/ApiTargetTest2$1$1.class" + )); + } + public void testSkipAndroidSupportInAospHalf() throws Exception { String expected; if (System.getenv("ANDROID_BUILD_TOP") != null) { diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data Binary files differnew file mode 100644 index 0000000..f18f226 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data Binary files differnew file mode 100644 index 0000000..ac3863e --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data Binary files differnew file mode 100644 index 0000000..f51c54b --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data Binary files differnew file mode 100644 index 0000000..defae98 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt new file mode 100644 index 0000000..7574805 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt @@ -0,0 +1,39 @@ +package test.pkg; + +import android.annotation.TargetApi; +import android.widget.GridLayout; + +// Test using the @TargetApi annotation on inner classes and anonymous inner classes +@SuppressWarnings("unused") +public class ApiTargetTest2 { + @TargetApi(value=14) + void foo2() { + new Runnable() { + @Override + public void run() { + new GridLayout(null, null, 0); + } + + void foo3() { + new Runnable() { + @Override + public void run() { + new GridLayout(null, null, 0); + } + }; + } + + @TargetApi(value=3) + void foo4() { + new Runnable() { + @Override + public void run() { + // This should be marked as an error since the effective target API is 3 here + new GridLayout(null, null, 0); + } + }; + } + + }; + } +} |