diff options
10 files changed, 179 insertions, 7 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java index f8d6cd9..29c3da6 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java @@ -77,6 +77,8 @@ class DefaultSdkInfo extends SdkInfo { @Override @Nullable public String getParentViewName(@NonNull String name) { + name = getRawType(name); + return PARENTS.get(name); } @@ -102,6 +104,9 @@ class DefaultSdkInfo extends SdkInfo { @Override public boolean isSubViewOf(@NonNull String parent, @NonNull String child) { + parent = getRawType(parent); + child = getRawType(child); + // Do analysis just on non-fqcn paths if (parent.indexOf('.') != -1) { parent = parent.substring(parent.lastIndexOf('.') + 1); @@ -128,13 +133,25 @@ class DefaultSdkInfo extends SdkInfo { return false; } + // Strip off type parameters, e.g. AdapterView<?> => AdapterView + private static String getRawType(String type) { + if (type != null) { + int index = type.indexOf('<'); + if (index != -1) { + type = type.substring(0, index); + } + } + + return type; + } + private static final int CLASS_COUNT = 59; @NonNull private static final Map<String, String> PARENTS = new HashMap<String, String>(CLASS_COUNT); static { - PARENTS.put(COMPOUND_BUTTON, VIEW); + PARENTS.put(COMPOUND_BUTTON, BUTTON); PARENTS.put(ABS_SPINNER, ADAPTER_VIEW); PARENTS.put(ABS_LIST_VIEW, ADAPTER_VIEW); PARENTS.put(ABS_SEEK_BAR, ADAPTER_VIEW); @@ -201,9 +218,7 @@ class DefaultSdkInfo extends SdkInfo { /* // Check that all widgets lead to the root view - boolean assertionsEnabled = false; - assert assertionsEnabled = true; // Intentional side-effect - if (assertionsEnabled) { + if (LintUtils.assertionsEnabled()) { for (String key : PARENTS.keySet()) { String parent = PARENTS.get(key); if (!parent.equals(VIEW)) { diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java index fb8e134..a2757cb 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java @@ -48,6 +48,7 @@ public class LintConstants { // Tags: Manifest public static final String TAG_SERVICE = "service"; //$NON-NLS-1$ public static final String TAG_USES_PERMISSION = "uses-permission";//$NON-NLS-1$ + public static final String TAG_USES_LIBRARY = "uses-library"; //$NON-NLS-1$ public static final String TAG_APPLICATION = "application"; //$NON-NLS-1$ public static final String TAG_INTENT_FILTER = "intent-filter"; //$NON-NLS-1$ public static final String TAG_USES_SDK = "uses-sdk"; //$NON-NLS-1$ diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java index 0232245..74ddd8a 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java @@ -53,7 +53,7 @@ public class BuiltinIssueRegistry extends IssueRegistry { private static final List<Issue> sIssues; static { - final int initialCapacity = 85; + final int initialCapacity = 86; List<Issue> issues = new ArrayList<Issue>(initialCapacity); issues.add(AccessibilityDetector.ISSUE); @@ -103,6 +103,7 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(ManifestOrderDetector.ORDER); issues.add(ManifestOrderDetector.USES_SDK); issues.add(ManifestOrderDetector.MULTIPLE_USES_SDK); + issues.add(ManifestOrderDetector.WRONG_PARENT); issues.add(SecurityDetector.EXPORTED_SERVICE); issues.add(SecurityDetector.OPEN_PROVIDER); issues.add(SecurityDetector.WORLD_READABLE); diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java index 95ca8b9..856bd26 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java @@ -20,7 +20,12 @@ import static com.android.tools.lint.detector.api.LintConstants.ANDROID_MANIFEST import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI; import static com.android.tools.lint.detector.api.LintConstants.ATTR_MIN_SDK_VERSION; import static com.android.tools.lint.detector.api.LintConstants.ATTR_TARGET_SDK_VERSION; +import static com.android.tools.lint.detector.api.LintConstants.TAG_ACTIVITY; import static com.android.tools.lint.detector.api.LintConstants.TAG_APPLICATION; +import static com.android.tools.lint.detector.api.LintConstants.TAG_PROVIDER; +import static com.android.tools.lint.detector.api.LintConstants.TAG_RECEIVER; +import static com.android.tools.lint.detector.api.LintConstants.TAG_SERVICE; +import static com.android.tools.lint.detector.api.LintConstants.TAG_USES_LIBRARY; import static com.android.tools.lint.detector.api.LintConstants.TAG_USES_PERMISSION; import static com.android.tools.lint.detector.api.LintConstants.TAG_USES_SDK; @@ -35,6 +40,7 @@ import com.android.tools.lint.detector.api.Speed; import com.android.tools.lint.detector.api.XmlContext; import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.File; @@ -93,11 +99,29 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann Category.CORRECTNESS, 6, - Severity.ERROR, + Severity.FATAL, ManifestOrderDetector.class, EnumSet.of(Scope.MANIFEST)).setMoreInfo( "http://developer.android.com/guide/topics/manifest/uses-sdk-element.html"); //$NON-NLS-1$ + /** Missing a {@code <uses-sdk>} element */ + public static final Issue WRONG_PARENT = Issue.create( + "WrongManifestParent", //$NON-NLS-1$ + "Checks that various manifest elements are declared in the right place", + + "The <uses-library> element should be defined as a direct child of the " + + "<application> tag, not the <manifest> tag or an <activity> tag. Similarly, " + + "a <uses-sdk> tag much be declared at the root level, and so on. This check " + + "looks for incorrect declaration locations in the manifest, and complains " + + "if an element is found in the wrong place.", + + Category.CORRECTNESS, + 6, + Severity.FATAL, + ManifestOrderDetector.class, + EnumSet.of(Scope.MANIFEST)).setMoreInfo( + "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$ + /** Constructs a new {@link ManifestOrderDetector} check */ public ManifestOrderDetector() { } @@ -148,13 +172,41 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann "uses-feature", //$NON-NLS-1$ "supports-screens", //$NON-NLS-1$ "compatible-screens", //$NON-NLS-1$ - "supports-gl-texture" //$NON-NLS-1$ + "supports-gl-texture", //$NON-NLS-1$ + TAG_USES_LIBRARY, + TAG_ACTIVITY, + TAG_SERVICE, + TAG_PROVIDER, + TAG_RECEIVER ); } @Override public void visitElement(XmlContext context, Element element) { String tag = element.getTagName(); + Node parentNode = element.getParentNode(); + + if (tag.equals(TAG_USES_LIBRARY) || tag.equals(TAG_ACTIVITY) || tag.equals(TAG_SERVICE) + || tag.equals(TAG_PROVIDER) || tag.equals(TAG_RECEIVER)) { + if (!TAG_APPLICATION.equals(parentNode.getNodeName()) + && context.isEnabled(WRONG_PARENT)) { + context.report(WRONG_PARENT, element, context.getLocation(element), + String.format( + "The <%1$s> element must be a direct child of the <application> element", + tag), null); + } + + return; + } + + if (parentNode != element.getOwnerDocument().getDocumentElement() + && context.isEnabled(WRONG_PARENT)) { + context.report(WRONG_PARENT, element, context.getLocation(element), + String.format( + "The <%1$s> element must be a direct child of the " + + "<manifest> root element", tag), null); + } + if (tag.equals(TAG_USES_SDK)) { mSeenUsesSdk++; diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java index 8ecee91..9553d78 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ManifestOrderDetectorTest.java @@ -77,4 +77,27 @@ public class ManifestOrderDetectorTest extends AbstractCheckTest { "multiplesdk.xml=>AndroidManifest.xml", "res/values/strings.xml")); } + + public void testWrongLocation() throws Exception { + assertEquals( + "AndroidManifest.xml:10: Error: The <permission> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:11: Error: The <permission-tree> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:12: Error: The <permission-group> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:14: Error: The <uses-sdk> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:15: Error: The <uses-configuration> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:16: Error: The <uses-feature> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:17: Error: The <supports-screens> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:18: Error: The <compatible-screens> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:19: Error: The <supports-gl-texture> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:24: Error: The <uses-library> element must be a direct child of the <application> element\n" + + "AndroidManifest.xml:25: Error: The <activity> element must be a direct child of the <application> element\n" + + "AndroidManifest.xml:8: Error: The <uses-sdk> element must be a direct child of the <manifest> root element\n" + + "AndroidManifest.xml:8: Warning: <uses-sdk> tag appears after <application> tag\n" + + "AndroidManifest.xml:8: Warning: <uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=\"?\"\n" + + "AndroidManifest.xml:9: Error: The <uses-permission> element must be a direct child of the <manifest> root element\n" + + "ManifestOrderDetectorTest_testWrongLocation/AndroidManifest.xml:14: Error: There should only be a single <uses-sdk> element in the manifest: merge these together\n" + + "=> ManifestOrderDetectorTest_testWrongLocation/AndroidManifest.xml:8: Also appears here", + + lintProject("broken-manifest2.xml=>AndroidManifest.xml")); + } } diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewTypeDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewTypeDetectorTest.java index f1f8492..2ea876f 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewTypeDetectorTest.java +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/ViewTypeDetectorTest.java @@ -34,4 +34,14 @@ public class ViewTypeDetectorTest extends AbstractCheckTest { "src/test/pkg/WrongCastActivity.java.txt=>src/test/pkg/WrongCastActivity.java" )); } + + public void test27441() throws Exception { + assertEquals( + "No warnings.", + + lintProject( + "res/layout/casts2.xml", + "src/test/pkg/WrongCastActivity2.java.txt=>src/test/pkg/WrongCastActivity2.java" + )); + } } diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/broken-manifest2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/broken-manifest2.xml new file mode 100644 index 0000000..307046b --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/broken-manifest2.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.helloworld" + android:versionCode="1" + android:versionName="1.0"> + <application android:icon="@drawable/icon" android:label="@string/app_name"> + <!-- Wrong declaration locations --> + <uses-sdk android:minSdkVersion="Froyo" /> + <uses-permission /> + <permission /> + <permission-tree /> + <permission-group /> + <instrumentation /> + <uses-sdk /> + <uses-configuration /> + <uses-feature /> + <supports-screens /> + <compatible-screens /> + <supports-gl-texture /> + + </application> + + <!-- Wrong declaration locations --> + <uses-library /> + <activity android:name=".HelloWorld" + android:label="@string/app_name" /> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/casts2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/casts2.xml new file mode 100644 index 0000000..249c02f --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/casts2.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- unit test from issue 27441 --> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <RadioGroup + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" > + + <RadioButton + android:id="@+id/additional" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <Spinner + android:id="@+id/reminder_lead" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </RadioGroup> + +</ScrollView> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt new file mode 100644 index 0000000..7cd422a --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt @@ -0,0 +1,15 @@ +package test.pkg; + +import android.app.*; +import android.view.*; +import android.widget.*; + +public class WrongCastActivity2 extends Activity { + private TextView additionalButton; + + private void configureAdditionalButton(View bodyView) { + this.additionalButton = (TextView) bodyView + .findViewById(R.id.additional); + Object x = (AdapterView<?>) bodyView.findViewById(R.id.reminder_lead); + } +} diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/client/api/DefaultSdkInfoTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/client/api/DefaultSdkInfoTest.java index 2ce41e4..afdc985 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/client/api/DefaultSdkInfoTest.java +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/client/api/DefaultSdkInfoTest.java @@ -42,6 +42,9 @@ public class DefaultSdkInfoTest extends TestCase { DefaultSdkInfo info = new DefaultSdkInfo(); assertTrue(info.isSubViewOf("Button", "Button")); assertTrue(info.isSubViewOf("TextView", "Button")); + assertTrue(info.isSubViewOf("TextView", "RadioButton")); + assertTrue(info.isSubViewOf("AdapterView", "Spinner")); + assertTrue(info.isSubViewOf("AdapterView<?>", "Spinner")); assertFalse(info.isSubViewOf("Button", "TextView")); assertFalse(info.isSubViewOf("CheckBox", "ToggleButton")); assertFalse(info.isSubViewOf("ToggleButton", "CheckBox")); @@ -52,5 +55,6 @@ public class DefaultSdkInfoTest extends TestCase { assertFalse(info.isSubViewOf("EditText", "TextView")); assertTrue(info.isSubViewOf("View", "TextView")); assertFalse(info.isSubViewOf("TextView", "View")); + assertFalse(info.isSubViewOf("Spinner", "AdapterView<?>")); } } |
