aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/src/com/android/util/PositionXmlParser.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java55
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java58
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java (renamed from eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressLintFix.java)8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java203
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java31
-rw-r--r--lint/cli/src/com/android/tools/lint/Main.java15
-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/LintConstants.java8
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java44
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ApiDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DeprecationDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ExtraTextDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedDebugModeDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java1
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java3
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/OverdrawDetector.java1
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java8
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/TypographyDetector.java26
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java16
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java4
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/Utf8Detector.java2
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java2
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java11
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/ignores.xml65
47 files changed, 608 insertions, 80 deletions
diff --git a/common/src/com/android/util/PositionXmlParser.java b/common/src/com/android/util/PositionXmlParser.java
index 54146db..052348d 100644
--- a/common/src/com/android/util/PositionXmlParser.java
+++ b/common/src/com/android/util/PositionXmlParser.java
@@ -145,7 +145,7 @@ public class PositionXmlParser {
// (see http://en.wikipedia.org/wiki/Byte_order_mark) so here we'll
// just skip those up to the XML prolog beginning character, <
xml = xml.replaceFirst("^([\\W]+)<","<"); //$NON-NLS-1$ //$NON-NLS-2$
- return parse(xml, null, false);
+ return parse(xml, new InputSource(new StringReader(xml)), false);
}
throw e;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
index d60d286..17a28c8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiAttributeNode.java
@@ -23,7 +23,10 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
import static com.android.ide.common.layout.LayoutConstants.ATTR_NAME;
import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
import static com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors.ATTR_COLOR;
+import static com.google.common.base.Strings.nullToEmpty;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
@@ -196,16 +199,52 @@ public abstract class UiAttributeNode implements Comparable<UiAttributeNode> {
* @return a negative number if name1 should be ordered before name2
*/
public static int compareAttributes(String name1, String name2) {
- int priority1 = getAttributePriority(name1);
- int priority2 = getAttributePriority(name2);
- if (priority1 != priority2) {
- return priority1 - priority2;
- }
-
- // Sort remaining attributes alphabetically
- return name1.compareTo(name2);
+ int priority1 = getAttributePriority(name1);
+ int priority2 = getAttributePriority(name2);
+ if (priority1 != priority2) {
+ return priority1 - priority2;
+ }
+
+ // Sort remaining attributes alphabetically
+ return name1.compareTo(name2);
}
+ /**
+ * Returns {@link Comparator} values for ordering attributes in the following
+ * order:
+ * <ul>
+ * <li> id
+ * <li> style
+ * <li> layout_width
+ * <li> layout_height
+ * <li> other layout params, sorted alphabetically
+ * <li> other attributes, sorted alphabetically, first by namespace, then by name
+ * </ul>
+ * @param prefix1 the namespace prefix, if any, of {@code name1}
+ * @param name1 the first attribute name to compare
+ * @param prefix2 the namespace prefix, if any, of {@code name2}
+ * @param name2 the second attribute name to compare
+ * @return a negative number if name1 should be ordered before name2
+ */
+ public static int compareAttributes(
+ @Nullable String prefix1, @NonNull String name1,
+ @Nullable String prefix2, @NonNull String name2) {
+ int priority1 = getAttributePriority(name1);
+ int priority2 = getAttributePriority(name2);
+ if (priority1 != priority2) {
+ return priority1 - priority2;
+ }
+
+ int namespaceDelta = nullToEmpty(prefix1).compareTo(nullToEmpty(prefix2));
+ if (namespaceDelta != 0) {
+ return namespaceDelta;
+ }
+
+ // Sort remaining attributes alphabetically
+ return name1.compareTo(name2);
+ }
+
+
/** Returns a sorting priority for the given attribute name */
private static int getAttributePriority(String name) {
if (ATTR_ID.equals(name)) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
index 40ebce5..a25d07b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
@@ -23,7 +23,9 @@ import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS_URI;
import static com.android.sdklib.SdkConstants.NS_RESOURCES;
+import static com.android.tools.lint.detector.api.LintConstants.XMLNS_PREFIX;
+import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.common.resources.platform.AttributeInfo;
@@ -1638,12 +1640,19 @@ public class UiElementNode implements IPropertySource {
return result;
}
- String firstName = dirtyAttributes.get(0).getDescriptor().getXmlLocalName();
+ AttributeDescriptor descriptor = dirtyAttributes.get(0).getDescriptor();
+ String firstName = descriptor.getXmlLocalName();
+ String firstNamePrefix = null;
+ if (descriptor.getNamespaceUri() != null) {
+ firstNamePrefix = lookupNamespacePrefix(element, descriptor.getNamespaceUri());
+ }
NamedNodeMap attributes = ((Element) element).getAttributes();
List<Attr> move = new ArrayList<Attr>();
for (int i = 0, n = attributes.getLength(); i < n; i++) {
Attr attribute = (Attr) attributes.item(i);
- if (UiAttributeNode.compareAttributes(attribute.getLocalName(), firstName) > 0) {
+ if (UiAttributeNode.compareAttributes(
+ attribute.getPrefix(), attribute.getLocalName(),
+ firstNamePrefix, firstName) > 0) {
move.add(attribute);
}
}
@@ -1724,6 +1733,25 @@ public class UiElementNode implements IPropertySource {
* @return The first prefix declared or the default "android" prefix.
*/
public static String lookupNamespacePrefix(Node node, String nsUri) {
+ String defaultPrefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_NAME : "ns"; //$NON-NLS-1$
+ return lookupNamespacePrefix(node, nsUri, defaultPrefix);
+ }
+
+ /**
+ * Returns the namespace prefix matching the requested namespace URI.
+ * If no such declaration is found, returns the default "android" prefix.
+ *
+ * @param node The current node. Must not be null.
+ * @param nsUri The namespace URI of which the prefix is to be found,
+ * e.g. SdkConstants.NS_RESOURCES
+ * @param defaultPrefix The default prefix (root) to use if the namespace
+ * is not found. If null, do not create a new namespace
+ * if this URI is not defined for the document.
+ * @return The first prefix declared or the provided prefix (possibly with
+ * a number appended to avoid conflicts with existing prefixes.
+ */
+ public static String lookupNamespacePrefix(
+ @Nullable Node node, @Nullable String nsUri, @Nullable String defaultPrefix) {
// Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java
// The following code emulates this simple call:
// String prefix = node.lookupPrefix(SdkConstants.NS_RESOURCES);
@@ -1771,12 +1799,17 @@ public class UiElementNode implements IPropertySource {
}
}
- // Failed the find a prefix. Generate a new sensible default prefix.
+ // Failed the find a prefix. Generate a new sensible default prefix, unless
+ // defaultPrefix was null in which case the caller does not want the document
+ // modified.
+ if (defaultPrefix == null) {
+ return null;
+ }
+
//
// We need to make sure the prefix is not one that was declared in the scope
- // visited above. Use a default namespace prefix "android" for the Android resource
- // NS and use "ns" for all other custom namespaces.
- String prefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_NAME : "ns"; //$NON-NLS-1$
+ // visited above. Pick a unique prefix from the provided default prefix.
+ String prefix = defaultPrefix;
String base = prefix;
for (int i = 1; visited.contains(prefix); i++) {
prefix = base + Integer.toString(i);
@@ -1789,9 +1822,18 @@ public class UiElementNode implements IPropertySource {
node = node.getNextSibling();
}
if (node != null) {
- Attr attr = doc.createAttributeNS(XMLNS_URI, prefix);
+ // This doesn't work:
+ //Attr attr = doc.createAttributeNS(XMLNS_URI, prefix);
+ //attr.setPrefix(XMLNS);
+ //
+ // Xerces throws
+ //org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or
+ // change an object in a way which is incorrect with regard to namespaces.
+ //
+ // Instead pass in the concatenated prefix. (This is covered by
+ // the UiElementNodeTest#testCreateNameSpace() test.)
+ Attr attr = doc.createAttributeNS(XMLNS_URI, XMLNS_PREFIX + prefix);
attr.setValue(nsUri);
- attr.setPrefix(XMLNS);
node.getAttributes().setNamedItemNS(attr);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressLintFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
index f23c9e5..3da617b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressLintFix.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAnnotation.java
@@ -71,7 +71,7 @@ import java.util.regex.Pattern;
* Marker resolution for adding {@code @SuppressLint} annotations in Java files.
* It can also add {@code @TargetApi} annotations.
*/
-class AddSuppressLintFix implements IMarkerResolution2 {
+class AddSuppressAnnotation implements IMarkerResolution2 {
private final IMarker mMarker;
private final String mId;
private final BodyDeclaration mNode;
@@ -80,7 +80,7 @@ class AddSuppressLintFix implements IMarkerResolution2 {
* If so pass a positive API number */
private final int mTargetApi;
- private AddSuppressLintFix(String id, IMarker marker, BodyDeclaration node,
+ private AddSuppressAnnotation(String id, IMarker marker, BodyDeclaration node,
String description, int targetApi) {
mId = id;
mMarker = marker;
@@ -335,14 +335,14 @@ class AddSuppressLintFix implements IMarkerResolution2 {
}
String desc = String.format("Add @SuppressLint '%1$s\' to '%2$s'", id, target);
- resolutions.add(new AddSuppressLintFix(id, marker, declaration, desc, -1));
+ resolutions.add(new AddSuppressAnnotation(id, marker, declaration, desc, -1));
if (api != -1
// @TargetApi is only valid on methods and classes, not fields etc
&& (body instanceof MethodDeclaration
|| body instanceof TypeDeclaration)) {
desc = String.format("Add @TargetApi(%1$d) to '%2$s'", api, target);
- resolutions.add(new AddSuppressLintFix(id, marker, declaration, desc, api));
+ resolutions.add(new AddSuppressAnnotation(id, marker, declaration, desc, api));
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
new file mode 100644
index 0000000..e3669f0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.lint;
+
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_IGNORE;
+import static com.android.tools.lint.detector.api.LintConstants.DOT_XML;
+import static com.android.tools.lint.detector.api.LintConstants.TOOLS_PREFIX;
+import static com.android.tools.lint.detector.api.LintConstants.TOOLS_URI;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.tools.lint.checks.ArraySizeDetector;
+import com.android.tools.lint.checks.DuplicateIdDetector;
+import com.android.tools.lint.checks.MergeRootFrameLayoutDetector;
+import com.android.tools.lint.checks.ObsoleteLayoutParamsDetector;
+import com.android.tools.lint.checks.OverdrawDetector;
+import com.android.tools.lint.checks.StringFormatDetector;
+import com.android.tools.lint.checks.TranslationDetector;
+import com.android.tools.lint.checks.WrongIdDetector;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Fix for adding {@code tools:ignore="id"} attributes in XML files.
+ */
+class AddSuppressAttribute implements ICompletionProposal {
+ private final AndroidXmlEditor mEditor;
+ private final String mId;
+ private final IMarker mMarker;
+ private final Element mElement;
+ private final String mDescription;
+
+ private AddSuppressAttribute(AndroidXmlEditor editor, String id, IMarker marker,
+ Element element, String description) {
+ mEditor = editor;
+ mId = id;
+ mMarker = marker;
+ mElement = element;
+ mDescription = description;
+ }
+
+ @Override
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+ @Override
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return mDescription;
+ }
+
+ @Override
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ @Override
+ public Image getImage() {
+ return IconFactory.getInstance().getIcon("newannotation"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void apply(IDocument document) {
+ mEditor.wrapUndoEditXmlModel("Suppress Lint Warning", new Runnable() {
+ @Override
+ public void run() {
+ String prefix = UiElementNode.lookupNamespacePrefix(mElement,
+ TOOLS_URI, null);
+ if (prefix == null) {
+ // Add in new prefix...
+ prefix = UiElementNode.lookupNamespacePrefix(mElement,
+ TOOLS_URI, TOOLS_PREFIX);
+ // ...and ensure that the header is formatted such that
+ // the XML namespace declaration is placed in the right
+ // position and wrapping is applied etc.
+ mEditor.scheduleNodeReformat(mEditor.getUiRootNode(),
+ true /*attributesOnly*/);
+ }
+
+ String ignore = mElement.getAttributeNS(TOOLS_URI, ATTR_IGNORE);
+ if (ignore.length() > 0) {
+ ignore = ignore + ',' + mId;
+ } else {
+ ignore = mId;
+ }
+
+ // Use the non-namespace form of set attribute since we can't
+ // reference the namespace until the model has been reloaded
+ mElement.setAttribute(prefix + ':' + ATTR_IGNORE, mId);
+
+ UiElementNode rootUiNode = mEditor.getUiRootNode();
+ if (rootUiNode != null) {
+ UiElementNode uiNode = rootUiNode.findXmlNode(mElement);
+ if (uiNode != null) {
+ mEditor.scheduleNodeReformat(uiNode, true /*attributesOnly*/);
+ }
+ }
+ }
+ });
+
+ try {
+ // Remove the marker now that the suppress attribute has been added
+ // (so the user doesn't have to re-run lint just to see it disappear)
+ mMarker.delete();
+ } catch (CoreException e) {
+ AdtPlugin.log(e, "Could not add suppress annotation");
+ }
+ }
+
+ /**
+ * Adds any applicable suppress lint fix resolutions into the given list
+ *
+ * @param editor the associated editor containing the marker
+ * @param marker the marker to create fixes for
+ * @param id the issue id
+ * @return a fix for this marker, or null if unable
+ */
+ @Nullable
+ public static AddSuppressAttribute createFix(
+ @NonNull AndroidXmlEditor editor,
+ @NonNull IMarker marker,
+ @NonNull String id) {
+ // This only applies to XML files:
+ String fileName = marker.getResource().getName();
+ if (!fileName.endsWith(DOT_XML)) {
+ return null;
+ }
+
+ int offset = marker.getAttribute(IMarker.CHAR_START, -1);
+ Node node = DomUtilities.getNode(editor.getStructuredDocument(), offset);
+ if (node == null) {
+ return null;
+ }
+ Document document = node.getOwnerDocument();
+ while (node != null && node.getNodeType() != Node.ELEMENT_NODE) {
+ node = node.getParentNode();
+ }
+
+ // Some issues cannot find a specific node scope associated with the error
+ // (for example because it involves cross-file analysis and at the end of
+ // the project scan when the warnings are computed the DOM model is no longer
+ // available). Until that's resolved, we need to filter these out such that
+ // we don't add misleading annotations on individual elements; the fallback
+ // path is the DOM document itself instead.
+ if (id.equals(ArraySizeDetector.INCONSISTENT.getId())
+ || id.equals(DuplicateIdDetector.CROSS_LAYOUT.getId())
+ || id.equals(MergeRootFrameLayoutDetector.ISSUE.getId())
+ || id.equals(ObsoleteLayoutParamsDetector.ISSUE.getId())
+ || id.equals(OverdrawDetector.ISSUE.getId())
+ || id.equals(StringFormatDetector.ARG_TYPES.getId())
+ || id.equals(StringFormatDetector.ARG_COUNT.getId())
+ || id.equals(TranslationDetector.MISSING.getId())
+ || id.equals(TranslationDetector.EXTRA.getId())
+ || id.equals(WrongIdDetector.UNKNOWN_ID.getId())
+ || id.equals(WrongIdDetector.UNKNOWN_ID_LAYOUT.getId())) {
+ node = document.getDocumentElement();
+ }
+
+ if (node == null) {
+ node = document.getDocumentElement();
+ if (node == null) {
+ return null;
+ }
+ }
+
+ String desc = String.format("Add ignore '%1$s\' to element", id);
+ Element element = (Element) node;
+ return new AddSuppressAttribute(editor, id, marker, element, desc);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java
index 85a0a48..ef2a265 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFixGenerator.java
@@ -116,7 +116,7 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
List<IMarkerResolution> resolutions = new ArrayList<IMarkerResolution>();
if (resource.getName().endsWith(DOT_JAVA)) {
- AddSuppressLintFix.createFixes(marker, id, resolutions);
+ AddSuppressAnnotation.createFixes(marker, id, resolutions);
}
resolutions.add(new MoreInfoProposal(id, marker.getAttribute(IMarker.MESSAGE, null)));
@@ -164,6 +164,7 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
for (IMarker marker : markers) {
String id = marker.getAttribute(EclipseLintRunner.MARKER_CHECKID_PROPERTY,
""); //$NON-NLS-1$
+
// TODO: Allow for more than one fix?
ICompletionProposal fix = LintFix.getFix(id, marker);
if (fix != null) {
@@ -173,6 +174,11 @@ public class LintFixGenerator implements IMarkerResolutionGenerator2, IQuickAssi
String message = marker.getAttribute(IMarker.MESSAGE, null);
proposals.add(new MoreInfoProposal(id, message));
+ fix = AddSuppressAttribute.createFix(editor, marker, id);
+ if (fix != null) {
+ proposals.add(fix);
+ }
+
proposals.add(new SuppressProposal(file, id, false));
proposals.add(new SuppressProposal(file.getProject(), id, true /* all */));
proposals.add(new SuppressProposal(file, id, true /* all */));
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java
index 8ec9adc..657b83a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AttributeSortOrder.java
@@ -37,6 +37,9 @@ public enum AttributeSortOrder {
public final String key;
+ /**
+ * @return a comparator for use by this attribute sort order
+ */
public Comparator<Attr> getAttributeComparator() {
switch (this) {
case ALPHABETICAL:
@@ -64,8 +67,9 @@ public enum AttributeSortOrder {
}
// Sort by preferred attribute order
- return UiAttributeNode.compareAttributes(attr1.getLocalName(),
- attr2.getLocalName());
+ return UiAttributeNode.compareAttributes(
+ attr1.getPrefix(), attr1.getLocalName(),
+ attr2.getPrefix(), attr2.getLocalName());
}
};
@@ -100,7 +104,9 @@ public enum AttributeSortOrder {
return 1;
}
- return attr1.getLocalName().compareTo(attr2.getLocalName());
+ // Sort by name rather than localname to ensure we sort by namespaces first,
+ // then by names.
+ return attr1.getName().compareTo(attr2.getName());
}
};
} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java
index a5149ae..43cba58 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java
@@ -17,10 +17,11 @@
package com.android.ide.eclipse.adt.internal.editors.manifest.model;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.TOOLS_URI;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.mock.MockXmlNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
@@ -36,6 +37,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
import junit.framework.TestCase;
+@SuppressWarnings("javadoc")
public class UiElementNodeTest extends TestCase {
private UiElementNode ui;
@@ -260,7 +262,6 @@ public class UiElementNodeTest extends TestCase {
assertEquals(0, second_permission.getAllUiAttributes().size());
}
-
public void testlookupNamespacePrefix() throws Exception {
// Setup
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
@@ -286,5 +287,31 @@ public class UiElementNodeTest extends TestCase {
String prefix = UiElementNode.lookupNamespacePrefix(baz, ANDROID_URI);
assertEquals("customPrefix", prefix);
+
+ prefix = UiElementNode.lookupNamespacePrefix(baz, TOOLS_URI, "tools");
+ assertEquals("tools", prefix);
+ }
+
+ public void testCreateNameSpace() throws Exception {
+ // Setup
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.newDocument();
+ Element rootElement = document.createElement("root");
+ document.appendChild(rootElement);
+ Element root = document.getDocumentElement();
+ root.appendChild(document.createTextNode(" "));
+ Element foo = document.createElement("foo");
+ root.appendChild(foo);
+ root.appendChild(document.createTextNode(" "));
+ Element bar = document.createElement("bar");
+ root.appendChild(bar);
+ Element baz = document.createElement("baz");
+ root.appendChild(baz);
+
+ String prefix = UiElementNode.lookupNamespacePrefix(baz, ANDROID_URI);
+ assertEquals("android", prefix);
}
}
diff --git a/lint/cli/src/com/android/tools/lint/Main.java b/lint/cli/src/com/android/tools/lint/Main.java
index ae1aa36..d319648 100644
--- a/lint/cli/src/com/android/tools/lint/Main.java
+++ b/lint/cli/src/com/android/tools/lint/Main.java
@@ -501,10 +501,11 @@ public class Main extends LintClient {
"Lint errors can be suppressed in a variety of ways:\n" +
"\n" +
"1. With a @SuppressLint annotation in the Java code\n" +
- "2. With a lint.xml configuration file in the project\n" +
- "3. With a lint.xml configuration file passed to lint " +
+ "2. With a tools:ignore attribute in the XML file\n" +
+ "3. With a lint.xml configuration file in the project\n" +
+ "4. With a lint.xml configuration file passed to lint " +
"via the " + ARG_CONFIG + " flag\n" +
- "4. With the " + ARG_IGNORE + " flag passed to lint.\n" +
+ "5. With the " + ARG_IGNORE + " flag passed to lint.\n" +
"\n" +
"To suppress a lint warning with an annotation, add " +
"a @SuppressLint(\"id\") annotation on the class, method " +
@@ -514,6 +515,14 @@ public class Main extends LintClient {
"\"UnusedIds\"}, or it can be \"all\" to suppress all lint " +
"warnings in the given scope.\n" +
"\n" +
+ "To suppress a lint warning in an XML file, add a " +
+ "tools:ignore=\"id\" attribute on the element containing " +
+ "the error, or one of its surrounding elements. You also " +
+ "need to define the namespace for the tools prefix on the " +
+ "root element in your document, next to the xmlns:android " +
+ "declaration:\n" +
+ "* xmlns:tools=\"http://schemas.android.com/tools\"\n" +
+ "\n" +
"To suppress lint warnings with a configuration XML file, " +
"create a file named lint.xml and place it at the root " +
"directory of the project in which it applies. (If you " +
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 0a8f532..c02d735 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
@@ -17,6 +17,7 @@
package com.android.tools.lint.client.api;
import static com.android.tools.lint.detector.api.LintConstants.ANDROID_MANIFEST_XML;
+import static com.android.tools.lint.detector.api.LintConstants.ATTR_IGNORE;
import static com.android.tools.lint.detector.api.LintConstants.DOT_CLASS;
import static com.android.tools.lint.detector.api.LintConstants.DOT_JAR;
import static com.android.tools.lint.detector.api.LintConstants.DOT_JAVA;
@@ -24,6 +25,7 @@ import static com.android.tools.lint.detector.api.LintConstants.PROGUARD_CFG;
import static com.android.tools.lint.detector.api.LintConstants.RES_FOLDER;
import static com.android.tools.lint.detector.api.LintConstants.SUPPRESS_ALL;
import static com.android.tools.lint.detector.api.LintConstants.SUPPRESS_LINT;
+import static com.android.tools.lint.detector.api.LintConstants.TOOLS_URI;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -53,6 +55,8 @@ import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
import java.io.File;
import java.io.FileInputStream;
@@ -1359,7 +1363,7 @@ public class LintDriver {
* @return true if there is a suppress annotation covering the specific
* issue in this class
*/
- public boolean isSuppressed(Issue issue, Node scope) {
+ public boolean isSuppressed(@NonNull Issue issue, @Nullable Node scope) {
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
@@ -1459,4 +1463,43 @@ public class LintDriver {
return false;
}
+
+ /**
+ * Returns whether the given issue is suppressed in the given XML DOM node.
+ *
+ * @param issue the issue to be checked, or null to just check for "all"
+ * @param node the DOM node containing the issue
+ * @return true if there is a suppress annotation covering the specific
+ * issue in this class
+ */
+ public boolean isSuppressed(@NonNull Issue issue, @Nullable org.w3c.dom.Node node) {
+ if (node instanceof Attr) {
+ node = ((Attr) node).getOwnerElement();
+ }
+ while (node != null) {
+ if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+ if (element.hasAttributeNS(TOOLS_URI, ATTR_IGNORE)) {
+ String ignore = element.getAttributeNS(TOOLS_URI, ATTR_IGNORE);
+ if (ignore.indexOf(',') == -1) {
+ if (ignore.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null
+ && issue.getId().equalsIgnoreCase(ignore))) {
+ return true;
+ }
+ } else {
+ for (String id : ignore.split(",")) { //$NON-NLS-1$
+ if (id.equalsIgnoreCase(SUPPRESS_ALL) || (issue != null
+ && issue.getId().equalsIgnoreCase(id))) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ node = node.getParentNode();
+ }
+
+ return false;
+ }
}
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 db0fb9f..e1e3c54 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
@@ -30,6 +30,11 @@ public class LintConstants {
/** Namespace used in XML files for Android attributes */
public static final String ANDROID_URI =
"http://schemas.android.com/apk/res/android"; //$NON-NLS-1$
+ /** Namespace used in XML files for Android Tooling attributes */
+ public static final String TOOLS_URI =
+ "http://schemas.android.com/tools"; //$NON-NLS-1$
+ /** Default prefix used for tools attributes */
+ public static final String TOOLS_PREFIX = "tools"; //$NON-NLS-1$
public static final String XMLNS_PREFIX = "xmlns:"; //$NON-NLS-1$
// Tags: Manifest
@@ -261,6 +266,9 @@ public class LintConstants {
public static final String R_DRAWABLE_PREFIX = "R.drawable."; //$NON-NLS-1$
public static final String R_ATTR_PREFIX = "R.attr."; //$NON-NLS-1$
+ // Attributes related to tools
+ public final static String ATTR_IGNORE = "ignore"; //$NON-NLS-1$
+
// SuppressLint
public static final String SUPPRESS_ALL = "all"; //$NON-NLS-1$
public static final String SUPPRESS_LINT = "SuppressLint"; //$NON-NLS-1$
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java
index 8936abc..d6a24ab 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/XmlContext.java
@@ -73,4 +73,48 @@ public class XmlContext extends Context {
return Location.create(file);
}
+
+ /**
+ * Reports an issue applicable to a given DOM node. The DOM node is used as the
+ * scope to check for suppress lint annotations.
+ *
+ * @param issue the issue to report
+ * @param scope the DOM node scope the error applies to. The lint infrastructure
+ * will check whether there are suppress directives on this node (or its enclosing
+ * nodes) and if so suppress the warning without involving the client.
+ * @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
+ */
+ public void report(
+ @NonNull Issue issue,
+ @Nullable Node scope,
+ @Nullable Location location,
+ @NonNull String message,
+ @Nullable Object data) {
+ if (scope != null && mDriver.isSuppressed(issue, scope)) {
+ return;
+ }
+ mDriver.getClient().report(this, issue, location, message, data);
+ }
+
+ @Override
+ public void report(
+ @NonNull Issue issue,
+ @Nullable Location location,
+ @NonNull String message,
+ @Nullable Object data) {
+ // Warn if clients use the non-scoped form? No, there are cases where an
+ // XML detector's error isn't applicable to one particular location (or it's
+ // not feasible to compute it cheaply)
+ //mDriver.getClient().log(null, "Warning: Issue " + issue
+ // + " was reported without a scope node: Can't be suppressed.");
+
+ // For now just check the document root itself
+ if (document != null && mDriver.isSuppressed(issue, document)) {
+ return;
+ }
+
+ super.report(issue, location, message, data);
+ }
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java
index f5ef4a5..abc08b2 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/AccessibilityDetector.java
@@ -77,13 +77,13 @@ public class AccessibilityDetector extends LayoutDetector {
@Override
public void visitElement(XmlContext context, Element element) {
if (!element.hasAttributeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION)) {
- context.report(ISSUE, context.getLocation(element),
+ context.report(ISSUE, element, context.getLocation(element),
"[Accessibility] Missing contentDescription attribute on image", null);
} else {
Attr attributeNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION);
String attribute = attributeNode.getValue();
if (attribute.length() == 0 || attribute.equals("TODO")) { //$NON-NLS-1$
- context.report(ISSUE, context.getLocation(attributeNode),
+ context.report(ISSUE, attributeNode, context.getLocation(attributeNode),
"[Accessibility] Empty contentDescription attribute on image", null);
}
}
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 e6fd2eb..7053038 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
@@ -137,7 +137,7 @@ public class ApiDetector extends LayoutDetector implements Detector.ClassScanner
String message = String.format(
"View requires API level %1$d (current min is %2$d): <%3$s>",
api, minSdk, tag);
- context.report(UNSUPPORTED, location, message, null);
+ context.report(UNSUPPORTED, element, location, message, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java
index b11fdc3..cf5cd4b 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ArraySizeDetector.java
@@ -140,6 +140,7 @@ public class ArraySizeDetector extends ResourceXmlDetector {
String otherName = otherFile.getParentFile().getName() + File.separator
+ otherFile.getName();
+ // TODO: Find applicable scope?
context.report(INCONSISTENT, location,
String.format(
"Array %1$s has an inconsistent number of items (%2$d in %3$s, %4$d in %5$s)",
@@ -155,7 +156,7 @@ public class ArraySizeDetector extends ResourceXmlDetector {
public void visitElement(XmlContext context, Element element) {
Attr attribute = element.getAttributeNode(ATTR_NAME);
if (attribute == null || attribute.getValue().length() == 0) {
- context.report(INCONSISTENT, context.getLocation(element),
+ context.report(INCONSISTENT, element, context.getLocation(element),
String.format("Missing name attribute in %1$s declaration", element.getTagName()),
null);
} else {
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java
index 2bd6a77..5a14f75 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ChildCountDetector.java
@@ -95,14 +95,14 @@ public class ChildCountDetector extends LayoutDetector {
String tagName = element.getTagName();
if (tagName.equals(SCROLL_VIEW) || tagName.equals(HORIZONTAL_SCROLL_VIEW)) {
if (childCount > 1 && getAccurateChildCount(element) > 1) {
- context.report(SCROLLVIEW_ISSUE,
+ context.report(SCROLLVIEW_ISSUE, element,
context.getLocation(element), "A scroll view can have only one child",
null);
}
} else {
// Adapter view
if (childCount > 0 && getAccurateChildCount(element) > 0) {
- context.report(ADAPTERVIEW_ISSUE,
+ context.report(ADAPTERVIEW_ISSUE, element,
context.getLocation(element),
"A list/grid should have no children declared in XML", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DeprecationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DeprecationDetector.java
index c6cf4e8..883afc7 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DeprecationDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DeprecationDetector.java
@@ -65,7 +65,7 @@ public class DeprecationDetector extends LayoutDetector {
@Override
public void visitElement(XmlContext context, Element element) {
- context.report(ISSUE, context.getLocation(element),
+ context.report(ISSUE, element, context.getLocation(element),
String.format("%1$s is deprecated", element.getTagName()), null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
index 5af5606..d0cf2d4 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
@@ -99,7 +99,7 @@ public class DetectMissingPrefix extends LayoutDetector {
return;
}
- context.report(MISSING_NAMESPACE,
+ context.report(MISSING_NAMESPACE, attribute,
context.getLocation(attribute),
"Attribute is missing the Android namespace prefix",
null);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
index 18e8b79..9c8ddea 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DuplicateIdDetector.java
@@ -347,6 +347,7 @@ public class DuplicateIdDetector extends LayoutDetector {
secondLocation.setMessage(String.format("%1$s originally defined here", id));
location.setSecondary(secondLocation);
}
+ // TODO: Get applicable scope?
context.report(CROSS_LAYOUT, location, msg, null);
}
@@ -404,7 +405,7 @@ public class DuplicateIdDetector extends LayoutDetector {
location.setSecondary(secondLocation);
}
- context.report(WITHIN_LAYOUT, location,
+ context.report(WITHIN_LAYOUT, attribute, location,
String.format("Duplicate id %1$s, already defined earlier in this layout",
id), null);
} else if (id.startsWith(NEW_ID_RESOURCE_PREFIX)) {
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExtraTextDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExtraTextDetector.java
index a14c7d0..e0665df 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExtraTextDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ExtraTextDetector.java
@@ -122,7 +122,7 @@ public class ExtraTextDetector extends ResourceXmlDetector {
location = Location.create(context.file, start, location.getEnd());
}
}
- context.report(ISSUE, location,
+ context.report(ISSUE, node, location,
String.format("Unexpected text found in layout file: \"%1$s\"",
snippet), null);
mFoundText = true;
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
index b19d537..a2788ab 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/GridLayoutDetector.java
@@ -95,7 +95,7 @@ public class GridLayoutDetector extends LayoutDetector {
int column = getInt(child, ATTR_LAYOUT_COLUMN, -1);
if (column >= declaredColumnCount) {
Attr node = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_COLUMN);
- context.report(ISSUE, context.getLocation(node),
+ context.report(ISSUE, node, context.getLocation(node),
String.format("Column attribute (%1$d) exceeds declared grid column count (%2$d)",
column, declaredColumnCount), null);
}
@@ -104,7 +104,7 @@ public class GridLayoutDetector extends LayoutDetector {
int row = getInt(child, ATTR_LAYOUT_ROW, -1);
if (row > declaredRowCount) {
Attr node = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_ROW);
- context.report(ISSUE, context.getLocation(node),
+ context.report(ISSUE, node, context.getLocation(node),
String.format("Row attribute (%1$d) exceeds declared grid row count (%2$d)",
row, declaredRowCount), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedDebugModeDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedDebugModeDetector.java
index 51a4285..306f546 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedDebugModeDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedDebugModeDetector.java
@@ -86,7 +86,7 @@ public class HardcodedDebugModeDetector extends Detector implements Detector.Xml
public void visitAttribute(XmlContext context, Attr attribute) {
if (attribute.getNamespaceURI().equals(ANDROID_URI)) {
//if (attribute.getOwnerElement().getTagName().equals(TAG_APPLICATION)) {
- context.report(ISSUE, context.getLocation(attribute),
+ context.report(ISSUE, attribute, context.getLocation(attribute),
"Avoid hardcoding the debug mode; leaving it out allows debug and " +
"release builds to automatically assign one", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java
index 816ecab..09a3ffc 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/HardcodedValuesDetector.java
@@ -90,7 +90,7 @@ public class HardcodedValuesDetector extends LayoutDetector {
return;
}
- context.report(ISSUE, context.getLocation(attribute),
+ context.report(ISSUE, attribute, context.getLocation(attribute),
String.format("[I18N] Hardcoded string \"%1$s\", should use @string resource",
value), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
index b8f2183..4dd7eb1 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/InefficientWeightDetector.java
@@ -134,7 +134,7 @@ public class InefficientWeightDetector extends LayoutDetector {
mInsideWeight.put(parent, Boolean.FALSE);
} else if (inside) {
Attr sizeNode = child.getAttributeNodeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT);
- context.report(NESTED_WEIGHTS,
+ context.report(NESTED_WEIGHTS, sizeNode,
context.getLocation(sizeNode),
"Nested weights are bad for performance", null);
// Don't warn again
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 f6973e5..281dccb 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
@@ -105,7 +105,7 @@ public class ManifestOrderDetector extends Detector implements Detector.XmlScann
if (tag.equals(TAG_APPLICATION)) {
mSeenApplication = true;
} else if (mSeenApplication) {
- context.report(ISSUE, context.getLocation(element),
+ context.report(ISSUE, element, context.getLocation(element),
String.format("<%1$s> tag appears after <application> tag", tag), null);
// Don't complain for *every* element following the <application> tag
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
index 2a67a9d..3f2320a 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MergeRootFrameLayoutDetector.java
@@ -122,6 +122,7 @@ public class MergeRootFrameLayoutDetector extends LayoutDetector implements Dete
if (mWhitelistedLayouts.contains(layout)) {
Handle handle = pair.getSecond();
Location location = handle.resolve();
+ // TODO: Compute applicable scope?
context.report(ISSUE, location,
"This <FrameLayout> can be replaced with a <merge> tag", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
index af240e2..9ca8017 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
@@ -123,7 +123,7 @@ public class NestedScrollingWidgetDetector extends LayoutDetector {
"horizontally scrolling widget (%2$s)";
}
String msg = String.format(format, parent.getTagName(), element.getTagName());
- context.report(ISSUE, context.getLocation(element), msg, null);
+ context.report(ISSUE, element, context.getLocation(element), msg, null);
}
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java
index 685e893..616a9ab 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ObsoleteLayoutParamsDetector.java
@@ -278,7 +278,7 @@ public class ObsoleteLayoutParamsDetector extends LayoutDetector {
&& isValidParamForParent(context, name, TABLE_ROW, parentTag)) {
return;
}
- context.report(ISSUE, context.getLocation(attribute),
+ context.report(ISSUE, attribute, context.getLocation(attribute),
String.format("Invalid layout param in a %1$s: %2$s", parentTag, name),
null);
}
@@ -369,6 +369,7 @@ public class ObsoleteLayoutParamsDetector extends LayoutDetector {
}
String message = String.format("Invalid layout param '%1$s' (%2$s)",
name, sb.toString());
+ // TODO: Compute applicable scope node
context.report(ISSUE, location, message, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverdrawDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverdrawDetector.java
index bad3c42..3d03e61 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverdrawDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/OverdrawDetector.java
@@ -211,6 +211,7 @@ public class OverdrawDetector extends LayoutDetector implements Detector.JavaSca
"Possible overdraw: Root element paints background %1$s with " +
"a theme that also paints a background (inferred theme is %2$s)",
drawable, theme);
+ // TODO: Compute applicable scope node
context.report(ISSUE, location, message, null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java
index 8b05aa3..7f9e859 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PrivateResourceDetector.java
@@ -65,7 +65,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector {
public void visitAttribute(XmlContext context, Attr attribute) {
String value = attribute.getNodeValue();
if (value.startsWith("@*android:")) { //$NON-NLS-1$
- context.report(ISSUE, context.getLocation(attribute),
+ context.report(ISSUE, attribute, context.getLocation(attribute),
"Illegal resource reference: @*android resources are private and " +
"not always present", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java
index 2caec16..cb37d5f 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/PxUsageDetector.java
@@ -76,7 +76,7 @@ public class PxUsageDetector extends LayoutDetector {
// 0px is fine. 0px is 0dp regardless of density...
return;
}
- context.report(ISSUE, context.getLocation(attribute),
+ context.report(ISSUE, attribute, context.getLocation(attribute),
"Avoid using \"px\" as units; use \"dp\" instead", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java
index 2d5e0e9..8ba2bc4 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ScrollViewChildDetector.java
@@ -87,7 +87,7 @@ public class ScrollViewChildDetector extends LayoutDetector {
if (VALUE_FILL_PARENT.equals(value) || VALUE_MATCH_PARENT.equals(value)) {
String msg = String.format("This %1$s should use android:%2$s=\"wrap_content\"",
child.getTagName(), attributeName);
- context.report(ISSUE, context.getLocation(sizeNode), msg,
+ context.report(ISSUE, sizeNode, context.getLocation(sizeNode), msg,
null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
index 7650cfd..1493131 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/SecurityDetector.java
@@ -167,7 +167,7 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner,
permission = application.getAttributeNS(ANDROID_URI, ATTR_PERMISSION);
if (permission == null || permission.length() == 0) {
// No declared permission for this exported service: complain
- context.report(EXPORTED_SERVICE,
+ context.report(EXPORTED_SERVICE, element,
context.getLocation(element),
"Exported service does not require permission", null);
}
@@ -183,14 +183,14 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner,
String msg = "Content provider shares everything; this is potentially dangerous.";
if (path != null && path.getValue().equals("/")) { //$NON-NLS-1$
- context.report(OPEN_PROVIDER, context.getLocation(path), msg, null);
+ context.report(OPEN_PROVIDER, path, context.getLocation(path), msg, null);
}
if (prefix != null && prefix.getValue().equals("/")) { //$NON-NLS-1$
- context.report(OPEN_PROVIDER, context.getLocation(prefix), msg, null);
+ context.report(OPEN_PROVIDER, prefix, context.getLocation(prefix), msg, null);
}
if (pattern != null && (pattern.getValue().equals("/") //$NON-NLS-1$
/* || pattern.getValue().equals(".*")*/)) {
- context.report(OPEN_PROVIDER, context.getLocation(pattern), msg, null);
+ context.report(OPEN_PROVIDER, pattern, context.getLocation(pattern), msg, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
index 85d3bfd..20fcf6b 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
@@ -98,7 +98,7 @@ public class StateListDetector extends ResourceXmlDetector {
}
}
if (!hasState) {
- context.report(ISSUE, context.getLocation(child),
+ context.report(ISSUE, child, context.getLocation(child),
String.format("No android:state_ attribute found on <item> %1$d, later states not reachable",
i), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
index 2ab3f57..c115fdf 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StringFormatDetector.java
@@ -400,6 +400,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto
currentFormat, format,
f.getParentFile().getName() + File.separator + f.getName());
warned = true;
+ // TODO: Compute applicable node scope
context.report(ARG_TYPES, location,
message, null);
break;
@@ -478,6 +479,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto
String message = String.format(
"Inconsistent number of arguments in formatting string %1$s; " +
"found both %2$d and %3$d", name, prevCount, count);
+ // TODO: Compute applicable node scope
context.report(ARG_COUNT, location,
message, null);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java
index 77de5ab..6856c14 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StyleCycleDetector.java
@@ -80,7 +80,7 @@ public class StyleCycleDetector extends ResourceXmlDetector {
String name = element.getAttribute(ATTR_NAME);
if (parent.endsWith(name) &&
parent.equals(STYLE_RESOURCE_PREFIX + name)) {
- context.report(ISSUE, context.getLocation(parentNode),
+ context.report(ISSUE, parentNode, context.getLocation(parentNode),
String.format("Style %1$s should not extend itself", name), null);
} else if (parent.startsWith(STYLE_RESOURCE_PREFIX)
&& parent.startsWith(name, STYLE_RESOURCE_PREFIX.length())
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java
index 0d52b83..2cc81ab 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TextFieldDetector.java
@@ -82,7 +82,7 @@ public class TextFieldDetector extends LayoutDetector {
return;
}
- context.report(ISSUE, context.getLocation(element),
+ context.report(ISSUE, element, context.getLocation(element),
"This text field does not specify an inputType or a hint", null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java
index 42e867b..257d06c 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TooManyViewsDetector.java
@@ -130,12 +130,12 @@ public class TooManyViewsDetector extends LayoutDetector {
mWarnedAboutDepth = true;
String msg = String.format("%1$s has more than %2$d levels, bad for performance",
context.file.getName(), MAX_DEPTH);
- context.report(TOO_DEEP, context.getLocation(element), msg, null);
+ context.report(TOO_DEEP, element, context.getLocation(element), msg, null);
}
if (mViewCount == MAX_VIEW_COUNT) {
String msg = String.format("%1$s has more than %2$d views, bad for performance",
context.file.getName(), MAX_VIEW_COUNT);
- context.report(TOO_MANY, context.getLocation(element), msg, null);
+ context.report(TOO_MANY, element, context.getLocation(element), msg, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
index ff8f886..41390d7 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TranslationDetector.java
@@ -315,6 +315,7 @@ public class TranslationDetector extends ResourceXmlDetector {
Collections.sort(sorted);
Location location = getLocation(language, parentFolderToLanguage);
int maxCount = context.getDriver().isAbbreviating() ? 4 : -1;
+ // TODO: Compute applicable node scope
context.report(MISSING, location,
String.format("Locale %1$s is missing translations for: %2$s",
language, LintUtils.formatList(sorted, maxCount)), null);
@@ -328,6 +329,7 @@ public class TranslationDetector extends ResourceXmlDetector {
Collections.sort(sorted);
Location location = getLocation(language, parentFolderToLanguage);
int maxCount = context.getDriver().isAbbreviating() ? 4 : -1;
+ // TODO: Compute applicable node scope
context.report(EXTRA, location, String.format(
"Locale %1$s is translating names not found in default locale: %2$s",
language, LintUtils.formatList(sorted, maxCount)), null);
@@ -357,7 +359,7 @@ public class TranslationDetector extends ResourceXmlDetector {
Attr attribute = element.getAttributeNode(ATTR_NAME);
if (attribute == null || attribute.getValue().length() == 0) {
- context.report(MISSING, context.getLocation(element),
+ context.report(MISSING, element, context.getLocation(element),
"Missing name attribute in <string> declaration", null);
} else {
String name = attribute.getValue();
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypographyDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypographyDetector.java
index 8f52e4c..bda828d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypographyDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypographyDetector.java
@@ -243,7 +243,7 @@ public class TypographyDetector extends ResourceXmlDetector {
// Replace ... with ellipsis character?
int ellipsis = text.indexOf("..."); //$NON-NLS-1$
if (ellipsis != -1 && !text.startsWith(".", ellipsis + 3)) { //$NON-NLS-1$
- context.report(ELLIPSIS, context.getLocation(element),
+ context.report(ELLIPSIS, element, context.getLocation(element),
ELLIPSIS_MESSAGE, null);
}
}
@@ -263,7 +263,7 @@ public class TypographyDetector extends ResourceXmlDetector {
Character.isWhitespace(matcher.group(1).charAt(
matcher.group(1).length() - 1));
if (!isNegativeNumber) {
- context.report(DASHES, context.getLocation(element),
+ context.report(DASHES, element, context.getLocation(element),
EN_DASH_MESSAGE,
null);
}
@@ -274,7 +274,7 @@ public class TypographyDetector extends ResourceXmlDetector {
// Don't suggest replacing -- or "--" with an m dash since these are sometimes
// used as digit marker strings
if (emdash > 1 && !text.startsWith("-", emdash + 2)) { //$NON-NLS-1$
- context.report(DASHES, context.getLocation(element),
+ context.report(DASHES, element, context.getLocation(element),
EM_DASH_MESSAGE, null);
}
}
@@ -288,7 +288,7 @@ public class TypographyDetector extends ResourceXmlDetector {
if (quoteEnd != -1 && quoteEnd > quoteStart + 1
&& (quoteEnd < text.length() -1 || quoteStart > 0)
&& SINGLE_QUOTE.matcher(text).matches()) {
- context.report(QUOTES, context.getLocation(element),
+ context.report(QUOTES, element, context.getLocation(element),
SINGLE_QUOTE_MESSAGE, null);
return;
}
@@ -296,7 +296,7 @@ public class TypographyDetector extends ResourceXmlDetector {
// Check for apostrophes that can be replaced by typographic apostrophes
if (quoteEnd == -1 && quoteStart > 0
&& Character.isLetterOrDigit(text.charAt(quoteStart - 1))) {
- context.report(QUOTES, context.getLocation(element),
+ context.report(QUOTES, element, context.getLocation(element),
TYPOGRAPHIC_APOSTROPHE_MESSAGE, null);
return;
}
@@ -308,7 +308,7 @@ public class TypographyDetector extends ResourceXmlDetector {
int quoteEnd = text.indexOf('"', quoteStart + 1);
if (quoteEnd != -1 && quoteEnd > quoteStart + 1) {
if (quoteEnd < text.length() -1 || quoteStart > 0) {
- context.report(QUOTES, context.getLocation(element),
+ context.report(QUOTES, element, context.getLocation(element),
DBL_QUOTES_MESSAGE, null);
return;
}
@@ -318,7 +318,7 @@ public class TypographyDetector extends ResourceXmlDetector {
// Check for grave accent quotations
if (text.indexOf('`') != -1 && GRAVE_QUOTATION.matcher(text).matches()) {
// Are we indenting ``like this'' or `this' ? If so, complain
- context.report(QUOTES, context.getLocation(element),
+ context.report(QUOTES, element, context.getLocation(element),
GRAVE_QUOTE_MESSAGE, null);
return;
}
@@ -337,19 +337,19 @@ public class TypographyDetector extends ResourceXmlDetector {
String top = matcher.group(1); // Numerator
String bottom = matcher.group(2); // Denominator
if (top.equals("1") && bottom.equals("2")) { //$NON-NLS-1$ //$NON-NLS-2$
- context.report(FRACTIONS, context.getLocation(element),
+ context.report(FRACTIONS, element, context.getLocation(element),
String.format(FRACTION_MESSAGE, '\u00BD', "&#189;", "1/2"), null);
} else if (top.equals("1") && bottom.equals("4")) { //$NON-NLS-1$ //$NON-NLS-2$
- context.report(FRACTIONS, context.getLocation(element),
+ context.report(FRACTIONS, element, context.getLocation(element),
String.format(FRACTION_MESSAGE, '\u00BC', "&#188;", "1/4"), null);
} else if (top.equals("3") && bottom.equals("4")) { //$NON-NLS-1$ //$NON-NLS-2$
- context.report(FRACTIONS, context.getLocation(element),
+ context.report(FRACTIONS, element, context.getLocation(element),
String.format(FRACTION_MESSAGE, '\u00BE', "&#190;", "3/4"), null);
} else if (top.equals("1") && bottom.equals("3")) { //$NON-NLS-1$ //$NON-NLS-2$
- context.report(FRACTIONS, context.getLocation(element),
+ context.report(FRACTIONS, element, context.getLocation(element),
String.format(FRACTION_MESSAGE, '\u2153', "&#8531;", "1/3"), null);
} else if (top.equals("2") && bottom.equals("3")) { //$NON-NLS-1$ //$NON-NLS-2$
- context.report(FRACTIONS, context.getLocation(element),
+ context.report(FRACTIONS, element, context.getLocation(element),
String.format(FRACTION_MESSAGE, '\u2154', "&#8532;", "2/3"), null);
}
}
@@ -360,7 +360,7 @@ public class TypographyDetector extends ResourceXmlDetector {
if (text.indexOf('(') != -1
&& (text.contains("(c)") || text.contains("(C)"))) { //$NON-NLS-1$ //$NON-NLS-2$
// Suggest replacing with copyright symbol?
- context.report(OTHER, context.getLocation(element),
+ context.report(OTHER, element, context.getLocation(element),
COPYRIGHT_MESSAGE, null);
// Replace (R) and TM as well? There are unicode characters for these but they
// are probably not very common within Android app strings.
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java
index a005eca..5ca97d0 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UnusedResourceDetector.java
@@ -276,13 +276,18 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
}
String message = String.format("The resource %1$s appears to be unused",
resource);
- Issue issue = resource.startsWith(R_ID_PREFIX) ? ISSUE_IDS : ISSUE;
+ Issue issue = getIssue(resource);
+ // TODO: Compute applicable node scope
context.report(issue, location, message, resource);
}
}
}
}
+ private static Issue getIssue(String resource) {
+ return resource.startsWith(R_ID_PREFIX) ? ISSUE_IDS : ISSUE;
+ }
+
private void recordLocation(String resource, Location location) {
Location oldLocation = mUnused.get(resource);
if (oldLocation != null) {
@@ -332,6 +337,10 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
} else {
assert context.getPhase() == 2;
if (mUnused.containsKey(resource)) {
+ if (context.getDriver().isSuppressed(getIssue(resource), item)) {
+ mUnused.remove(resource);
+ return;
+ }
recordLocation(resource, context.getLocation(item));
}
}
@@ -385,7 +394,12 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec
if (context.getPhase() == 1) {
mDeclarations.add(resource);
} else if (mUnused.containsKey(resource)) {
+ if (context.getDriver().isSuppressed(getIssue(resource), attribute)) {
+ mUnused.remove(resource);
+ return;
+ }
recordLocation(resource, context.getLocation(attribute));
+ return;
}
} else if (mReferences != null) {
if (value.startsWith("@") //$NON-NLS-1$
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java
index fb5c159..8ded0e6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UseCompoundDrawableDetector.java
@@ -86,7 +86,7 @@ public class UseCompoundDrawableDetector extends LayoutDetector {
((second.getTagName().equals(IMAGE_VIEW) &&
first.getTagName().equals(TEXT_VIEW) &&
!second.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_WEIGHT)))) {
- context.report(ISSUE, context.getLocation(element),
+ context.report(ISSUE, element, context.getLocation(element),
"This tag and its children can be replaced by one <TextView/> and " +
"a compound drawable", null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java
index e231320..1b4f855 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/UselessViewDetector.java
@@ -204,7 +204,7 @@ public class UselessViewDetector extends LayoutDetector {
format += "; transfer the background attribute to the other view";
}
String message = String.format(format, tag, parentTag);
- context.report(USELESS_PARENT, location, message, null);
+ context.report(USELESS_PARENT, element, location, message, null);
}
// This is the old UselessView check from layoutopt
@@ -234,6 +234,6 @@ public class UselessViewDetector extends LayoutDetector {
String tag = element.getTagName();
String message = String.format(
"This %1$s view is useless (no children, no background, no id, no style)", tag);
- context.report(USELESS_LEAF, location, message, null);
+ context.report(USELESS_LEAF, element, location, message, null);
}
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/Utf8Detector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/Utf8Detector.java
index 97c0f43..6a87e94 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/Utf8Detector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/Utf8Detector.java
@@ -91,7 +91,7 @@ public class Utf8Detector extends LayoutDetector {
String encoding = matcher.group(1);
Location location = Location.create(context.file, xml,
matcher.start(1), matcher.end(1));
- context.report(ISSUE, location, String.format(
+ context.report(ISSUE, null, location, String.format(
"%1$s: Not using UTF-8 as the file encoding. This can lead to subtle " +
"bugs with non-ascii characters", encoding), null);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java
index e63b925..823eabd 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/WrongIdDetector.java
@@ -229,6 +229,7 @@ public class WrongIdDetector extends LayoutDetector {
"The id \"%1$s\" is not defined anywhere.%2$s",
id, suggestionMessage);
}
+ // TODO: Compute applicable node scope
context.report(UNKNOWN_ID, location, message, null);
} else if (checkSameLayout && (!projectScope || isBound)) {
// The id was defined, but in a different layout. Usually not intentional
@@ -236,6 +237,7 @@ public class WrongIdDetector extends LayoutDetector {
// name.)
Handle handle = pair.getSecond();
Location location = handle.resolve();
+ // TODO: Compute applicable node scope
context.report(UNKNOWN_ID_LAYOUT, location,
String.format(
"The id \"%1$s\" is not referring to any views in this layout",
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
index 7005686..06ff9b6 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
@@ -33,4 +33,15 @@ public class HardcodedValuesDetectorTest extends AbstractCheckTest {
"should use @string resource",
lintFiles("res/layout/accessibility.xml"));
}
+
+ public void testSuppress() throws Exception {
+ // All but one errors in the file contain ignore attributes - direct, inherited
+ // and lists
+ assertEquals(
+ "ignores.xml:61: Warning: [I18N] Hardcoded string \"Hardcoded\", should use " +
+ "@string resource",
+
+ lintFiles("res/layout/ignores.xml"));
+ }
+
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/ignores.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/ignores.xml
new file mode 100644
index 0000000..d4be910
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/ignores.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/newlinear"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <!-- Ignored via attribute, should be hidden -->
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button1"
+ tools:ignore="HardcodedText" >
+ </Button>
+
+ <!-- Inherited ignore from parent -->
+
+ <LinearLayout
+ android:id="@+id/parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:ignore="HardcodedText" >
+
+ <Button
+ android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button2" >
+ </Button>
+ </LinearLayout>
+
+ <!-- Hardcoded text warning ignored through "all" -->
+
+ <Button
+ android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button3"
+ tools:ignore="all" >
+ </Button>
+
+ <!-- Ignored through item in ignore list -->
+
+ <Button
+ android:id="@+id/button4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Hardcoded"
+ tools:ignore="NewApi,HardcodedText" >
+ </Button>
+
+ <!-- Not ignored: should show up as a warning -->
+
+ <Button
+ android:id="@+id/button5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Hardcoded"
+ tools:ignore="Other" >
+ </Button>
+
+</LinearLayout>