diff options
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 251a65e..da274f6 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 Binary files differnew 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 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; + } +} |