aboutsummaryrefslogtreecommitdiffstats
path: root/lint/libs
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-12-03 17:54:49 -0800
committerTor Norbye <tnorbye@google.com>2012-12-03 18:18:35 -0800
commitcf7a066f41b4eb1d63f176e0958a7f2c64d740cb (patch)
treee49dbe6690cc32c6a46c6753d1dddc3250d876d9 /lint/libs
parentb227943031143adf72fb9d4fca8483403ebe61e4 (diff)
downloadsdk-cf7a066f41b4eb1d63f176e0958a7f2c64d740cb.zip
sdk-cf7a066f41b4eb1d63f176e0958a7f2c64d740cb.tar.gz
sdk-cf7a066f41b4eb1d63f176e0958a7f2c64d740cb.tar.bz2
40876: Lint should flag "extension" pattern specifiers in SimpleDateFormat
Change-Id: I427e498a314aeaeb2a302811bfde1dc719e435d5
Diffstat (limited to 'lint/libs')
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java43
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/LocaleDetector.java2
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ApiDetectorTest.java56
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.databin0 -> 1044 bytes
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt30
5 files changed, 109 insertions, 22 deletions
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 46e330e..323ec37 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
@@ -514,11 +514,16 @@ public class ApiDetector extends ResourceXmlDetector implements Detector.ClassSc
// For virtual dispatch, walk up the inheritance chain checking
// each inherited method
if (owner.startsWith("android/") //$NON-NLS-1$
- || owner.startsWith("java/") //$NON-NLS-1$
|| owner.startsWith("javax/")) { //$NON-NLS-1$
// The API map has already inlined all inherited methods
// so no need to keep checking up the chain
owner = null;
+ } else if (owner.startsWith("java/")) { //$NON-NLS-1$
+ if (owner.equals(LocaleDetector.DATE_FORMAT_OWNER)) {
+ checkSimpleDateFormat(context, method, node, minSdk);
+ }
+ // Already inlined; see comment above
+ owner = null;
} else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) {
owner = context.getDriver().getSuperClass(owner);
} else if (node.getOpcode() == Opcodes.INVOKESTATIC && api == -1) {
@@ -570,6 +575,42 @@ public class ApiDetector extends ResourceXmlDetector implements Detector.ClassSc
}
}
+ private void checkSimpleDateFormat(ClassContext context, MethodNode method,
+ MethodInsnNode node, int minSdk) {
+ if (minSdk >= 9) {
+ // Already OK
+ return;
+ }
+ if (node.name.equals(CONSTRUCTOR_NAME) && !node.desc.equals("()V")) { //$NON-NLS-1$
+ // Check first argument
+ AbstractInsnNode prev = LintUtils.getPrevInstruction(node);
+ if (prev != null && !node.desc.equals("(Ljava/lang/String;)V")) { //$NON-NLS-1$
+ prev = LintUtils.getPrevInstruction(prev);
+ }
+ if (prev != null && prev.getOpcode() == Opcodes.LDC) {
+ LdcInsnNode ldc = (LdcInsnNode) prev;
+ Object cst = ldc.cst;
+ if (cst instanceof String) {
+ String pattern = (String) cst;
+ boolean isEscaped = false;
+ for (int i = 0; i < pattern.length(); i++) {
+ char c = pattern.charAt(i);
+ if (c == '\'') {
+ isEscaped = !isEscaped;
+ } else if (!isEscaped && (c == 'L' || c == 'c')) {
+ String message = String.format(
+ "The pattern character '%1$c' requires API level 9 (current " +
+ "min is %2$d) : \"%3$s\"", c, minSdk, pattern);
+ report(context, message, node, method, pattern, null,
+ SearchHints.create(FORWARD));
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
@SuppressWarnings("rawtypes") // ASM API
private boolean methodDefinedLocally(ClassNode classNode, String name, String desc) {
List methodList = classNode.methods;
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 6a6ed9a..1b34494 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
@@ -99,7 +99,7 @@ public class LocaleDetector extends Detector implements ClassScanner {
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$
+ static final String DATE_FORMAT_OWNER = "java/text/SimpleDateFormat"; //$NON-NLS-1$
private static final String STRING_OWNER = "java/lang/String"; //$NON-NLS-1$
/** Constructs a new {@link LocaleDetector} */
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 aa62f58..e42280f 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
@@ -463,26 +463,6 @@ public class ApiDetectorTest extends AbstractCheckTest {
));
}
- public void testSkipAndroidSupportInAospHalf() throws Exception {
- String expected;
- if (System.getenv("ANDROID_BUILD_TOP") != null) {
- expected = "No warnings.";
- } else {
- expected = "bin/classes/android/support/foo/Foo.class: Error: Class requires API level 8 (current min is 1): org.w3c.dom.DOMError [NewApi]\n" +
- "1 errors, 0 warnings\n";
- }
-
- assertEquals(
- expected,
-
- lintProject(
- "apicheck/classpath=>.classpath",
- "apicheck/minsdk1.xml=>AndroidManifest.xml",
- "apicheck/ApiCallTest2.java.txt=>src/src/android/support/foo/Foo.java",
- "apicheck/ApiCallTest2.class.data=>bin/classes/android/support/foo/Foo.class"
- ));
- }
-
public void testSuper() throws Exception {
// See http://code.google.com/p/android/issues/detail?id=36384
assertEquals(
@@ -700,4 +680,40 @@ public class ApiDetectorTest extends AbstractCheckTest {
"apicheck/ApiCallTest11$MyActivity.class.data=>bin/classes/test/pkg/ApiCallTest11$MyActivity.class"
));
}
+
+ public void testDateFormat() throws Exception {
+ // See http://code.google.com/p/android/issues/detail?id=40876
+ assertEquals(
+ "src/test/pkg/ApiCallTest12.java:18: Error: Call requires API level 9 (current min is 4): java.text.DateFormatSymbols#getInstance [NewApi]\n" +
+ " new SimpleDateFormat(\"yyyy-MM-dd\", DateFormatSymbols.getInstance());\n" +
+ " ~~~~~~~~~~~\n" +
+ "src/test/pkg/ApiCallTest12.java:23: Error: The pattern character 'L' requires API level 9 (current min is 4) : \"yyyy-MM-dd LL\" [NewApi]\n" +
+ " new SimpleDateFormat(\"yyyy-MM-dd LL\", Locale.US);\n" +
+ " ^\n" +
+ "src/test/pkg/ApiCallTest12.java:25: Error: The pattern character 'c' requires API level 9 (current min is 4) : \"cc yyyy-MM-dd\" [NewApi]\n" +
+ " SimpleDateFormat format = new SimpleDateFormat(\"cc yyyy-MM-dd\");\n" +
+ " ^\n" +
+ "3 errors, 0 warnings\n",
+
+ lintProject(
+ "apicheck/classpath=>.classpath",
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "project.properties1=>project.properties",
+ "apicheck/ApiCallTest12.java.txt=>src/test/pkg/ApiCallTest12.java",
+ "apicheck/ApiCallTest12.class.data=>bin/classes/test/pkg/ApiCallTest12.class"
+ ));
+ }
+
+ public void testDateFormatOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "apicheck/classpath=>.classpath",
+ "apicheck/minsdk10.xml=>AndroidManifest.xml",
+ "project.properties1=>project.properties",
+ "apicheck/ApiCallTest12.java.txt=>src/test/pkg/ApiCallTest12.java",
+ "apicheck/ApiCallTest12.class.data=>bin/classes/test/pkg/ApiCallTest12.class"
+ ));
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
new file mode 100644
index 0000000..4056133
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
Binary files differ
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
new file mode 100644
index 0000000..6ac71db
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
@@ -0,0 +1,30 @@
+package test.pkg;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+@SuppressWarnings({ "unused", "javadoc" })
+@SuppressLint("SimpleDateFormat")
+public class ApiCallTest12 {
+ public void test() {
+ // Normal SimpleDateFormat calls
+ new SimpleDateFormat();
+ new SimpleDateFormat("yyyy-MM-dd");
+ new SimpleDateFormat("yyyy-MM-dd", DateFormatSymbols.getInstance());
+ new SimpleDateFormat("yyyy-MM-dd", Locale.US);
+ new SimpleDateFormat("MMMM", Locale.US);
+
+ // Flag format strings requiring API 9
+ new SimpleDateFormat("yyyy-MM-dd LL", Locale.US);
+
+ SimpleDateFormat format = new SimpleDateFormat("cc yyyy-MM-dd");
+
+ // Escaped text
+ new SimpleDateFormat("MM-dd 'My Location'", Locale.US);
+ }
+}