diff options
Diffstat (limited to 'lint/libs/lint_api')
-rw-r--r-- | lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java | 82 | ||||
-rw-r--r-- | lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java | 27 |
2 files changed, 108 insertions, 1 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java index a749ace..d328ab8 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 @@ -31,6 +31,7 @@ import static org.objectweb.asm.Opcodes.ASM4; import com.android.annotations.NonNull; import com.android.annotations.Nullable; +import com.android.annotations.VisibleForTesting; import com.android.resources.ResourceFolderType; import com.android.tools.lint.client.api.LintListener.EventType; import com.android.tools.lint.detector.api.Category; @@ -66,10 +67,12 @@ import org.w3c.dom.Element; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -860,6 +863,7 @@ public class LintDriver { if (libraries.size() > 0) { libraryEntries = new ArrayList<ClassEntry>(64); findClasses(libraryEntries, libraries); + Collections.sort(libraryEntries); } else { libraryEntries = Collections.emptyList(); } @@ -877,6 +881,7 @@ public class LintDriver { } else { classEntries = new ArrayList<ClassEntry>(64); findClasses(classEntries, classFolders); + Collections.sort(classEntries); } if (getPhase() == 1) { @@ -894,15 +899,35 @@ public class LintDriver { runClassDetectors(Scope.CLASS_FILE, classEntries, project, main); } + /** + * Stack of {@link ClassNode} nodes for outer classes of the currently + * processed class, including that class itself. Populated by + * {@link #runClassDetectors(Scope, List, Project, Project)} and used by + * {@link #getOuterClassNode(ClassNode)} + */ + private Deque<ClassNode> mOuterClasses; + private void runClassDetectors(Scope scope, List<ClassEntry> entries, Project project, Project main) { if (mScope.contains(scope)) { List<Detector> classDetectors = mScopeDetectors.get(scope); if (classDetectors != null && classDetectors.size() > 0 && entries.size() > 0) { + mOuterClasses = new ArrayDeque<ClassNode>(); for (ClassEntry entry : entries) { ClassReader reader = new ClassReader(entry.bytes); ClassNode classNode = new ClassNode(); reader.accept(classNode, 0 /* flags */); + + ClassNode peek; + while ((peek = mOuterClasses.peek()) != null) { + if (classNode.name.startsWith(peek.name)) { + break; + } else { + mOuterClasses.pop(); + } + } + mOuterClasses.push(classNode); + if (isSuppressed(null, classNode)) { // Class was annotated with suppress all -- no need to look any further continue; @@ -917,8 +942,31 @@ public class LintDriver { return; } } + + mOuterClasses = null; + } + } + } + + /** Returns the outer class node of the given class node + * @param classNode the inner class node + * @return the outer class node */ + public ClassNode getOuterClassNode(@NonNull ClassNode classNode) { + String outerName = classNode.outerClass; + + Iterator<ClassNode> iterator = mOuterClasses.iterator(); + while (iterator.hasNext()) { + ClassNode node = iterator.next(); + if (outerName != null) { + if (node.name.equals(outerName)) { + return node; + } + } else if (node == classNode) { + return iterator.hasNext() ? iterator.next() : null; } } + + return null; } private Map<String, String> getSuperMap(List<ClassEntry> libraryEntries, @@ -1665,7 +1713,8 @@ public class LintDriver { } /** A pending class to be analyzed by {@link #checkClasses} */ - private static class ClassEntry { + @VisibleForTesting + static class ClassEntry implements Comparable<ClassEntry> { public final File file; public final File jarFile; public final File binDir; @@ -1678,5 +1727,36 @@ public class LintDriver { this.binDir = binDir; this.bytes = bytes; } + + @Override + public int compareTo(ClassEntry other) { + String p1 = file.getPath(); + String p2 = other.file.getPath(); + int m1 = p1.length(); + int m2 = p2.length(); + int m = Math.min(m1, m2); + + for (int i = 0; i < m; i++) { + char c1 = p1.charAt(i); + char c2 = p2.charAt(i); + if (c1 != c2) { + // Sort Foo$Bar.class *after* Foo.class, even though $ < . + if (c1 == '.' && c2 == '$') { + return -1; + } + if (c1 == '$' && c2 == '.') { + return 1; + } + return c1 - c2; + } + } + + return (m == m1) ? -1 : 1; + } + + @Override + public String toString() { + return file.getPath(); + } } } diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java index 9de6dc8..ff05270 100644 --- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java +++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/ClassContext.java @@ -257,6 +257,33 @@ public class ClassContext extends Context { if (mDriver.isSuppressed(issue, mClassNode)) { return; } + ClassNode curr = mClassNode; + while (curr != null) { + curr = mDriver.getOuterClassNode(curr); + if (curr != null) { + if (mClassNode.outerMethod != null) { + @SuppressWarnings("rawtypes") // ASM API + List methods = curr.methods; + for (Object m : methods) { + MethodNode method = (MethodNode) m; + if (method.name.equals(mClassNode.outerMethod) + && method.desc.equals(mClassNode.outerMethodDesc)) { + // Found the outer method for this anonymous class; continue + // reporting on it (which will also work its way up the parent + // class hierarchy) + if (method != null && mDriver.isSuppressed(issue, method)) { + return; + } + break; + } + } + } + if (mDriver.isSuppressed(issue, curr)) { + return; + } + } + } + super.report(issue, location, message, data); } |