aboutsummaryrefslogtreecommitdiffstats
path: root/lint
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-10-18 11:52:53 -0700
committerTor Norbye <tnorbye@google.com>2012-10-18 19:30:35 -0700
commit5c02dc822f23fbf44fb05994ae016585cda57a4b (patch)
tree0bdcda685fbe3185b7ea043d934bc378f61c8c89 /lint
parentb8bd06476ddbaa2cf745270e1223508407505217 (diff)
downloadsdk-5c02dc822f23fbf44fb05994ae016585cda57a4b.zip
sdk-5c02dc822f23fbf44fb05994ae016585cda57a4b.tar.gz
sdk-5c02dc822f23fbf44fb05994ae016585cda57a4b.tar.bz2
Fix handling of @SuppressLint on fields
When you have a field initialization like this: private int foo = new ForbiddenClass(); the actual code to perform the initialization lives in a method named <init> (or for a static field, <clinit>). If you tried to suppress lint errors here by adding an annotation on the field, it would not be found by lint, since lint looks at method level annotations and it's sitting on the field node instead. To fix this, the suppress check needs to identify the field scenario, and in that case (when checking whether an error is suppressed) look up the field in the class and check annotations there. There was a second bug: The lint check which looks for invalid suppress annotation sites (since they're not allowed on local variables for class based lint checks) incorrectly concluded that these types of initializations were local variables rather than field initializations. This fixes issue 38626: ADT: Misleading lint rule: SimpleDateFormat Change-Id: I254f3fb5a6132d6cbe39bd425ffe6d67ed7b84ed
Diffstat (limited to 'lint')
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java45
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java23
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/AnnotationDetector.java22
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java5
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java1
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/LocaleDetector.java9
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/OverrideDetector.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewTagDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java7
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AnnotationDetectorTest.java14
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java18
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.databin0 -> 732 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt19
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt16
17 files changed, 157 insertions, 34 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
index 7f6c0ee..fc9487d 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
@@ -60,8 +60,10 @@ import com.google.common.io.Closeables;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.w3c.dom.Attr;
@@ -1800,27 +1802,62 @@ public class LintDriver {
// pointers, so we have to have multiple methods which pass in each type
// of node (class, method, field) to be checked.
- // TODO: The Quickfix should look for lint warnings placed *inside* warnings
- // and warn that they won't apply to checks that are bytecode oriented!
-
/**
* Returns whether the given issue is suppressed in the given method.
*
* @param issue the issue to be checked, or null to just check for "all"
+ * @param classNode the class containing the issue
* @param method the method containing the issue
+ * @param instruction the instruction within the method, if any
* @return true if there is a suppress annotation covering the specific
* issue on this method
*/
- public boolean isSuppressed(@Nullable Issue issue, @NonNull MethodNode method) {
+ public boolean isSuppressed(
+ @Nullable Issue issue,
+ @NonNull ClassNode classNode,
+ @NonNull MethodNode method,
+ @Nullable AbstractInsnNode instruction) {
if (method.invisibleAnnotations != null) {
@SuppressWarnings("unchecked")
List<AnnotationNode> annotations = method.invisibleAnnotations;
return isSuppressed(issue, annotations);
}
+ // Initializations of fields end up placed in generated methods (<init>
+ // for members and <clinit> for static fields).
+ if (instruction != null && method.name.charAt(0) == '<') {
+ AbstractInsnNode next = LintUtils.getNextInstruction(instruction);
+ if (next != null && next.getType() == AbstractInsnNode.FIELD_INSN) {
+ FieldInsnNode fieldRef = (FieldInsnNode) next;
+ FieldNode field = findField(classNode, fieldRef.owner, fieldRef.name);
+ if (field != null && isSuppressed(issue, field)) {
+ return true;
+ }
+ }
+ }
+
return false;
}
+ @Nullable
+ private FieldNode findField(ClassNode classNode, String owner, String name) {
+ while (classNode != null) {
+ if (owner.equals(classNode.name)) {
+ @SuppressWarnings("rawtypes") // ASM API
+ List fieldList = classNode.fields;
+ for (Object f : fieldList) {
+ FieldNode field = (FieldNode) f;
+ if (field.name.equals(name)) {
+ return field;
+ }
+ }
+ return null;
+ }
+ classNode = getOuterClassNode(classNode);
+ }
+ return null;
+ }
+
/**
* Returns whether the given issue is suppressed for the given field.
*
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 68c25d9..159180d 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
@@ -280,7 +280,7 @@ public class ClassContext extends Context {
* Detectors should only call this method if an error applies to the whole class
* scope and there is no specific method or field that applies to the error.
* If so, use
- * {@link #report(Issue, MethodNode, Location, String, Object)} or
+ * {@link #report(Issue, MethodNode, AbstractInsnNode, Location, String, Object)} or
* {@link #report(Issue, FieldNode, Location, String, Object)}, such that
* suppress annotations are checked.
*
@@ -313,7 +313,8 @@ public class ClassContext extends Context {
// Found the outer method for this anonymous class; continue
// reporting on it (which will also work its way up the parent
// class hierarchy)
- if (method != null && mDriver.isSuppressed(issue, method)) {
+ if (method != null && mDriver.isSuppressed(issue, mClassNode, method,
+ null)) {
return;
}
break;
@@ -337,9 +338,18 @@ public class ClassContext extends Context {
* Reports an issue applicable to a given method node.
*
* @param issue the issue to report
- * @param method the method scope the error applies to. The lint infrastructure
- * will check whether there are suppress annotations on this method (or its enclosing
- * class) and if so suppress the warning without involving the client.
+ * @param method the method scope the error applies to. The lint
+ * infrastructure will check whether there are suppress
+ * annotations on this method (or its enclosing class) and if so
+ * suppress the warning without involving the client.
+ * @param instruction the instruction within the method the error applies
+ * to. You cannot place annotations on individual method
+ * instructions (for example, annotations on local variables are
+ * allowed, but are not kept in the .class file). However, this
+ * instruction is needed to handle suppressing errors on field
+ * initializations; in that case, the errors may be reported in
+ * the {@code <clinit>} method, but the annotation is found not
+ * on that method but for the {@link FieldNode}'s.
* @param location the location of the issue, or null if not known
* @param message the message for this warning
* @param data any associated data, or null
@@ -347,10 +357,11 @@ public class ClassContext extends Context {
public void report(
@NonNull Issue issue,
@Nullable MethodNode method,
+ @Nullable AbstractInsnNode instruction,
@Nullable Location location,
@NonNull String message,
@Nullable Object data) {
- if (method != null && mDriver.isSuppressed(issue, method)) {
+ if (method != null && mDriver.isSuppressed(issue, mClassNode, method, instruction)) {
return;
}
report(issue, location, message, data); // also checks the class node
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/AnnotationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/AnnotationDetector.java
index 7543def..9982f18 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/AnnotationDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/AnnotationDetector.java
@@ -40,12 +40,16 @@ import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.ArrayInitializer;
import lombok.ast.AstVisitor;
+import lombok.ast.Block;
+import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.MethodDeclaration;
import lombok.ast.Modifiers;
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
+import lombok.ast.TypeBody;
import lombok.ast.VariableDefinition;
/**
@@ -153,11 +157,27 @@ public class AnnotationDetector extends Detector implements Detector.JavaScanner
IssueRegistry registry = mContext.getDriver().getRegistry();
Issue issue = registry.getIssue(id);
if (issue != null && !issue.getScope().contains(Scope.JAVA_FILE)) {
+ // Ensure that this isn't a field
+ Node parent = node.getParent();
+ while (parent != null) {
+ if (parent instanceof MethodDeclaration
+ || parent instanceof ConstructorDeclaration
+ || parent instanceof Block) {
+ break;
+ } else if (parent instanceof TypeBody) { // It's a field
+ return true;
+ }
+ parent = parent.getParent();
+ if (parent == null) {
+ return true;
+ }
+ }
+
// This issue doesn't have AST access: annotations are not
// available for local variables or parameters
mContext.report(ISSUE,mContext.getLocation(node), String.format(
"The @SuppresLint annotation cannot be used on a local" +
- " variable with the lint check '%1$s': move out to the " +
+ " variable with the lint check '%1$s': move out to the " +
"surrounding method", id),
null);
return false;
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 5e1a687..26ff804 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
@@ -683,7 +683,8 @@ public class ApiDetector extends ResourceXmlDetector implements Detector.ClassSc
}
}
- Location location = context.getLocationForLine(lineNumber, patternStart, patternEnd, hints);
- context.report(UNSUPPORTED, method, location, message, null);
+ Location location = context.getLocationForLine(lineNumber, patternStart, patternEnd,
+ hints);
+ context.report(UNSUPPORTED, method, node, location, message, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java
index 4b5bc84..a38cd02 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java
@@ -302,7 +302,7 @@ public class ButtonDetector extends ResourceXmlDetector {
reportOkPosition(context, element);
}
} else {
- assert BACK_LABEL.equalsIgnoreCase(label);
+ assert BACK_LABEL.equalsIgnoreCase(label) : label + ':' + context.file;
Location location = context.getLocation(element);
if (context.isEnabled(BACKBUTTON)) {
context.report(BACKBUTTON, element, location,
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
index a1fd9fa..a74b8ec 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/FieldGetterDetector.java
@@ -165,7 +165,8 @@ public class FieldGetterDetector extends Detector implements Detector.ClassScann
if (fieldName == null) {
fieldName = "";
}
- context.report(ISSUE, entry.method, location, String.format(
+ context.report(ISSUE, entry.method, entry.call, location,
+ String.format(
"Calling getter method %1$s() on self is " +
"slower than field access (%2$s)", getter, fieldName), fieldName);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
index 34a697c..6ee9b1b 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/JavaPerformanceDetector.java
@@ -180,7 +180,6 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc
mCheckAllocations = context.isEnabled(PAINT_ALLOC);
mCheckMaps = context.isEnabled(USE_SPARSEARRAY);
mCheckValueOf = context.isEnabled(USE_VALUEOF);
- assert mCheckAllocations || mCheckMaps || mCheckValueOf; // enforced by infrastructure
}
@Override
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/LocaleDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/LocaleDetector.java
index 43e9ea0..6a6ed9a 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/LocaleDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/LocaleDetector.java
@@ -96,7 +96,7 @@ public class LocaleDetector extends Detector implements ClassScanner {
6,
Severity.WARNING,
LocaleDetector.class,
- EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.CLASS_FILE)).setMoreInfo(
+ Scope.CLASS_FILE_SCOPE).setMoreInfo(
"http://developer.android.com/reference/java/text/SimpleDateFormat.html"); //$NON-NLS-1$
private static final String DATE_FORMAT_OWNER = "java/text/SimpleDateFormat"; //$NON-NLS-1$
@@ -147,8 +147,9 @@ public class LocaleDetector extends Detector implements ClassScanner {
"To get local formatting use getDateInstance(), getDateTimeInstance(), " +
"or getTimeInstance(), or use new SimpleDateFormat(String template, " +
"Locale locale) with for example Locale.US for ASCII dates.", name);
- context.report(DATE_FORMAT, method, location, message, null);
+ context.report(DATE_FORMAT, method, call, location, message, null);
}
+ return;
} else if (!owner.equals(STRING_OWNER)) {
return;
}
@@ -186,7 +187,7 @@ public class LocaleDetector extends Detector implements ClassScanner {
String message =
"Implicitly using the default locale is a common source of bugs: " +
"Use String.format(Locale, ...) instead";
- context.report(STRING_LOCALE, method, location, message, null);
+ context.report(STRING_LOCALE, method, call, location, message, null);
}
}
} catch (AnalyzerException e) {
@@ -198,7 +199,7 @@ public class LocaleDetector extends Detector implements ClassScanner {
String message = String.format(
"Implicitly using the default locale is a common source of bugs: " +
"Use %1$s(Locale) instead", name);
- context.report(STRING_LOCALE, method, location, message, null);
+ context.report(STRING_LOCALE, method, call, location, message, null);
}
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
index 65eff86..000b139 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MathDetector.java
@@ -90,7 +90,7 @@ public class MathDetector extends Detector implements Detector.ClassScanner {
String message = String.format(
"Use java.lang.Math#%1$s instead of android.util.FloatMath#%1$s() " +
"since it is faster as of API 8", call.name);
- context.report(ISSUE, method, context.getLocation(call), message, null /*data*/);
+ context.report(ISSUE, method, call, context.getLocation(call), message, null /*data*/);
}
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverrideDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverrideDetector.java
index 137bc21..041536f 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverrideDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverrideDetector.java
@@ -251,7 +251,8 @@ public class OverrideDetector extends Detector implements ClassScanner {
String signature = method.name + method.desc;
if (methods.containsKey(signature)){
- if (method != null && context.getDriver().isSuppressed(ISSUE, method)) {
+ if (method != null && context.getDriver().isSuppressed(ISSUE, classNode,
+ method, null)) {
Map<String, String> errors = mErrors.get(classNode.name);
if (errors != null) {
errors.remove(signature);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewTagDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewTagDetector.java
index e944427..3590f83 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewTagDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ViewTagDetector.java
@@ -164,7 +164,7 @@ public class ViewTagDetector extends Detector implements ClassScanner {
String message = String.format("Avoid setting %1$s as values for setTag: " +
"Can lead to memory leaks in versions older than Android 4.0",
objectType);
- context.report(ISSUE, method, location, message, null);
+ context.report(ISSUE, method, call, location, message, null);
}
} catch (AnalyzerException e) {
context.log(e, null);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java
index ccf8d73..bddee0f 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WakelockDetector.java
@@ -117,7 +117,7 @@ public class WakelockDetector extends Detector implements ClassScanner {
if (context.getDriver().getPhase() == 2) {
assert !mHasRelease;
- context.report(ISSUE, method, context.getLocation(call),
+ context.report(ISSUE, method, call, context.getLocation(call),
"Found a wakelock acquire() but no release() calls anywhere",
null);
} else {
@@ -136,7 +136,7 @@ public class WakelockDetector extends Detector implements ClassScanner {
if ("onDestroy".equals(method.name) //$NON-NLS-1$
&& context.getDriver().isSubclassOf(
classNode, ANDROID_APP_ACTIVITY)) {
- context.report(ISSUE, method, context.getLocation(call),
+ context.report(ISSUE, method, call, context.getLocation(call),
"Wakelocks should be released in onPause, not onDestroy",
null);
}
@@ -191,7 +191,8 @@ public class WakelockDetector extends Detector implements ClassScanner {
message = "The release() call is not always reached";
}
- context.report(ISSUE, method, context.getLocation(release), message, null);
+ context.report(ISSUE, method, acquire,
+ context.getLocation(release), message, null);
}
} catch (AnalyzerException e) {
context.log(e, null);
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AnnotationDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AnnotationDetectorTest.java
index 4c9d34d..becca80 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AnnotationDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AnnotationDetectorTest.java
@@ -25,20 +25,22 @@ import java.util.List;
public class AnnotationDetectorTest extends AbstractCheckTest {
public void test() throws Exception {
assertEquals(
- "src/test/pkg/WrongAnnotation.java:8: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @SuppressLint(\"NewApi\") // Invalid\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:11: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:13: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "4 errors, 0 warnings\n" +
- "",
+ "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppresLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ " @SuppressLint(\"NewApi\")\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "5 errors, 0 warnings\n",
lintProject(
"src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
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 576c0fe..af7c3d5 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
@@ -639,4 +639,22 @@ public class ApiDetectorTest extends AbstractCheckTest {
));
}
+ public void testSuppressFieldAnnotations() throws Exception {
+ // See http://code.google.com/p/android/issues/detail?id=38626
+ assertEquals(
+ "src/test/pkg/ApiCallTest9.java:9: Error: Call requires API level 14 (current min is 4): new android.widget.GridLayout [NewApi]\n" +
+ " private GridLayout field1 = new GridLayout(null);\n" +
+ " ~~~~~~~~~~\n" +
+ "src/test/pkg/ApiCallTest9.java:12: Error: Call requires API level 14 (current min is 4): new android.widget.GridLayout [NewApi]\n" +
+ " private static GridLayout field2 = new GridLayout(null);\n" +
+ " ~~~~~~~~~~\n" +
+ "2 errors, 0 warnings\n",
+
+ lintProject(
+ "apicheck/classpath=>.classpath",
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "apicheck/ApiCallTest9.java.txt=>src/test/pkg/ApiCallTest9.java",
+ "apicheck/ApiCallTest9.class.data=>bin/classes/test/pkg/ApiCallTest9.class"
+ ));
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
new file mode 100644
index 0000000..f50ebab
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
new file mode 100644
index 0000000..199231e
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
@@ -0,0 +1,19 @@
+package test.pkg;
+
+import android.annotation.SuppressLint;
+import android.widget.GridLayout;
+
+/** Test suppress on fields */
+public class ApiCallTest9 {
+ // Actual initialization code lives in the synthetic method <init>
+ private GridLayout field1 = new GridLayout(null);
+
+ // Actual initialization code lives in the synthetic method <clinit>
+ private static GridLayout field2 = new GridLayout(null);
+
+ @SuppressLint("NewApi")
+ private GridLayout field3 = new GridLayout(null);
+
+ @SuppressLint("NewApi")
+ private static GridLayout field4 = new GridLayout(null);
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
index 9256055..6fef833 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
@@ -1,6 +1,7 @@
-package com.example.test2;
+package test.pkg;
import android.annotation.SuppressLint;
+import android.view.View;
public class WrongAnnotation {
@Override
@@ -15,5 +16,16 @@ public class WrongAnnotation {
@SuppressLint("SdCardPath") // Valid: AST-based check
boolean d;
}
-}
+ @SuppressLint("NewApi")
+ private int field1;
+
+ @SuppressLint("NewApi")
+ private int field2 = 5;
+
+ static {
+ // Local variable outside method: invalid
+ @SuppressLint("NewApi")
+ int localvar = 5;
+ }
+}