Lint: Rewrite Lint diagnostics using Uast.

This commit is contained in:
Yan Zhulanow
2016-04-06 16:05:50 +03:00
parent 126cc778e4
commit 4df9e9261e
210 changed files with 6176 additions and 7039 deletions
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="guava" level="project" />
<orderEntry type="module" module-name="uast-common" />
<orderEntry type="module" module-name="uast-java" />
<orderEntry type="module" module-name="uast-android" />
<orderEntry type="library" name="android-plugin" level="project" />
<orderEntry type="library" name="intellij-core" level="project" />
<orderEntry type="module" module-name="android-annotations" />
</component>
</module>
@@ -14,25 +14,16 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.ClassScanner;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Detector.ClassScanner;
import com.google.common.annotations.Beta;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Specialized visitor for running detectors on a class object model.
@@ -90,9 +81,9 @@ class AsmVisitor {
if (names != null) {
checkFullClass = false;
for (String element : names) {
List<Detector.ClassScanner> list = mMethodNameToChecks.get(element);
List<ClassScanner> list = mMethodNameToChecks.get(element);
if (list == null) {
list = new ArrayList<Detector.ClassScanner>();
list = new ArrayList<ClassScanner>();
mMethodNameToChecks.put(element, list);
}
list.add(scanner);
@@ -103,9 +94,9 @@ class AsmVisitor {
if (owners != null) {
checkFullClass = false;
for (String element : owners) {
List<Detector.ClassScanner> list = mMethodOwnerToChecks.get(element);
List<ClassScanner> list = mMethodOwnerToChecks.get(element);
if (list == null) {
list = new ArrayList<Detector.ClassScanner>();
list = new ArrayList<ClassScanner>();
mMethodOwnerToChecks.put(element, list);
}
list.add(scanner);
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.google.common.annotations.Beta;
/**
@@ -14,11 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static org.objectweb.asm.Opcodes.ASM5;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -26,7 +22,6 @@ import com.android.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -40,6 +35,10 @@ import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static org.objectweb.asm.Opcodes.ASM5;
/** A class, present either as a .class file on disk, or inside a .jar file. */
@VisibleForTesting
class ClassEntry implements Comparable<ClassEntry> {
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.klint.detector.api.Issue;
import com.google.common.collect.Lists;
import java.util.List;
@@ -14,14 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.annotations.Beta;
/**
@@ -14,48 +14,32 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.CURRENT_PLATFORM;
import static com.android.SdkConstants.PLATFORM_WINDOWS;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.utils.XmlUtils;
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.*;
import org.xml.sax.SAXParseException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static com.android.SdkConstants.CURRENT_PLATFORM;
import static com.android.SdkConstants.PLATFORM_WINDOWS;
/**
* Default implementation of a {@link Configuration} which reads and writes
* configuration data into {@code lint.xml} in the project directory.
@@ -14,53 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ABSOLUTE_LAYOUT;
import static com.android.SdkConstants.ABS_LIST_VIEW;
import static com.android.SdkConstants.ABS_SEEK_BAR;
import static com.android.SdkConstants.ABS_SPINNER;
import static com.android.SdkConstants.ADAPTER_VIEW;
import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
import static com.android.SdkConstants.BUTTON;
import static com.android.SdkConstants.CHECKABLE;
import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
import static com.android.SdkConstants.CHECK_BOX;
import static com.android.SdkConstants.COMPOUND_BUTTON;
import static com.android.SdkConstants.EDIT_TEXT;
import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
import static com.android.SdkConstants.FRAME_LAYOUT;
import static com.android.SdkConstants.GALLERY;
import static com.android.SdkConstants.GRID_VIEW;
import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW;
import static com.android.SdkConstants.IMAGE_BUTTON;
import static com.android.SdkConstants.IMAGE_VIEW;
import static com.android.SdkConstants.LINEAR_LAYOUT;
import static com.android.SdkConstants.LIST_VIEW;
import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
import static com.android.SdkConstants.PROGRESS_BAR;
import static com.android.SdkConstants.RADIO_BUTTON;
import static com.android.SdkConstants.RADIO_GROUP;
import static com.android.SdkConstants.RELATIVE_LAYOUT;
import static com.android.SdkConstants.SCROLL_VIEW;
import static com.android.SdkConstants.SEEK_BAR;
import static com.android.SdkConstants.SPINNER;
import static com.android.SdkConstants.SURFACE_VIEW;
import static com.android.SdkConstants.SWITCH;
import static com.android.SdkConstants.TABLE_LAYOUT;
import static com.android.SdkConstants.TABLE_ROW;
import static com.android.SdkConstants.TAB_HOST;
import static com.android.SdkConstants.TAB_WIDGET;
import static com.android.SdkConstants.TEXT_VIEW;
import static com.android.SdkConstants.TOGGLE_BUTTON;
import static com.android.SdkConstants.VIEW;
import static com.android.SdkConstants.VIEW_ANIMATOR;
import static com.android.SdkConstants.VIEW_GROUP;
import static com.android.SdkConstants.VIEW_PKG_PREFIX;
import static com.android.SdkConstants.VIEW_STUB;
import static com.android.SdkConstants.VIEW_SWITCHER;
import static com.android.SdkConstants.WEB_VIEW;
import static com.android.SdkConstants.WIDGET_PKG_PREFIX;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -72,6 +26,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static com.android.SdkConstants.*;
/**
* Default simple implementation of an {@link SdkInfo}
* <p>
@@ -14,28 +14,21 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* Registry which provides a list of checks to be performed on an Android project
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Severity;
import com.android.utils.SdkUtils;
import com.google.common.collect.Lists;
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.tools.klint.client.api;
import org.jetbrains.uast.UastConverter;
import org.jetbrains.uast.java.JavaConverter;
public class JavaLintLanguageExtension extends LintLanguageExtension {
@Override
public UastConverter getConverter() {
return JavaConverter.INSTANCE;
}
}
@@ -14,26 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ATTR_VALUE;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.klint.detector.api.JavaContext;
import com.google.common.annotations.Beta;
import com.google.common.base.Splitter;
import java.util.Collections;
import java.util.List;
import lombok.ast.Identifier;
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.TypeReference;
import lombok.ast.TypeReferencePart;
import org.jetbrains.uast.UFile;
/**
* A wrapper for a Java parser. This allows tools integrating lint to map directly
@@ -56,475 +42,11 @@ public abstract class JavaParser {
public static final String TYPE_BYTE = "byte"; //$NON-NLS-1$
public static final String TYPE_NULL = "null"; //$NON-NLS-1$
/**
* Prepare to parse the given contexts. This method will be called before
* a series of {@link #parseJava(JavaContext)} calls, which allows some
* parsers to do up front global computation in case they want to more
* efficiently process multiple files at the same time. This allows a single
* type-attribution pass for example, which is a lot more efficient than
* performing global type analysis over and over again for each individual
* file
*
* @param contexts a list of contexts to be parsed
*/
public abstract void prepareJavaParse(@NonNull List<JavaContext> contexts);
/**
* Parse the file pointed to by the given context.
*
* @param context the context pointing to the file to be parsed, typically
* via {@link Context#getContents()} but the file handle (
* {@link Context#file} can also be used to map to an existing
* editor buffer in the surrounding tool, etc)
* @return the compilation unit node for the file
*/
@Nullable
public abstract Node parseJava(@NonNull JavaContext context);
/**
* Returns a {@link Location} for the given node
*
* @param context information about the file being parsed
* @param node the node to create a location for
* @return a location for the given node
*/
@NonNull
public abstract Location getLocation(@NonNull JavaContext context, @NonNull Node node);
/**
* Creates a light-weight handle to a location for the given node. It can be
* turned into a full fledged location by
* {@link com.android.tools.lint.detector.api.Location.Handle#resolve()}.
*
* @param context the context providing the node
* @param node the node (element or attribute) to create a location handle
* for
* @return a location handle
*/
@NonNull
public abstract Location.Handle createLocationHandle(@NonNull JavaContext context,
@NonNull Node node);
/**
* Dispose any data structures held for the given context.
* @param context information about the file previously parsed
* @param compilationUnit the compilation unit being disposed
*/
public void dispose(@NonNull JavaContext context, @NonNull Node compilationUnit) {
}
/**
* Dispose any remaining data structures held for all contexts.
* Typically frees up any resources allocated by
* {@link #prepareJavaParse(List)}
*/
public void dispose() {
}
/**
* Resolves the given expression node: computes the declaration for the given symbol
*
* @param context information about the file being parsed
* @param node the node to resolve
* @return a node representing the resolved fully type: class/interface/annotation,
* field, method or variable
*/
@Nullable
public abstract ResolvedNode resolve(@NonNull JavaContext context, @NonNull Node node);
/**
* Finds the given type, if possible (which should be reachable from the compilation
* patch of the given node.
*
* @param context information about the file being parsed
* @param fullyQualifiedName the fully qualified name of the class to look up
* @return the class, or null if not found
*/
@Nullable
public ResolvedClass findClass(
@NonNull JavaContext context,
@NonNull String fullyQualifiedName) {
return null;
}
/**
* Gets the type of the given node
*
* @param context information about the file being parsed
* @param node the node to look up the type for
* @return the type of the node, if known
*/
@Nullable
public abstract TypeDescriptor getType(@NonNull JavaContext context, @NonNull Node node);
/** A description of a type, such as a primitive int or the android.app.Activity class */
public abstract static class TypeDescriptor {
/**
* Returns the fully qualified name of the type, such as "int" or "android.app.Activity"
* */
@NonNull public abstract String getName();
/**
* Returns the full signature of the type, which is normally the same as {@link #getName()}
* but for arrays can include []'s, for generic methods can include generics parameters
* etc
*/
@NonNull public abstract String getSignature();
public abstract boolean matchesName(@NonNull String name);
public abstract boolean matchesSignature(@NonNull String signature);
@NonNull
public TypeReference getNode() {
TypeReference typeReference = new TypeReference();
StrictListAccessor<TypeReferencePart, TypeReference> parts = typeReference.astParts();
for (String part : Splitter.on('.').split(getName())) {
Identifier identifier = Identifier.of(part);
parts.addToEnd(new TypeReferencePart().astIdentifier(identifier));
}
return typeReference;
}
/** If the type is not primitive, returns the class of the type if known */
@Nullable
public abstract ResolvedClass getTypeClass();
@Override
public abstract boolean equals(Object o);
}
/** Convenience implementation of {@link TypeDescriptor} */
public static class DefaultTypeDescriptor extends TypeDescriptor {
private String mName;
public DefaultTypeDescriptor(String name) {
mName = name;
}
@NonNull
@Override
public String getName() {
return mName;
}
@NonNull
@Override
public String getSignature() {
return getName();
}
@Override
public boolean matchesName(@NonNull String name) {
return mName.equals(name);
}
@Override
public boolean matchesSignature(@NonNull String signature) {
return matchesName(signature);
}
@Override
public String toString() {
return getSignature();
}
@Override
@Nullable
public ResolvedClass getTypeClass() {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DefaultTypeDescriptor that = (DefaultTypeDescriptor) o;
return !(mName != null ? !mName.equals(that.mName) : that.mName != null);
}
@Override
public int hashCode() {
return mName != null ? mName.hashCode() : 0;
}
}
/** A resolved declaration from an AST Node reference */
public abstract static class ResolvedNode {
@NonNull
public abstract String getName();
/** Returns the signature of the resolved node */
public abstract String getSignature();
public abstract int getModifiers();
@Override
public String toString() {
return getSignature();
}
/** Returns any annotations defined on this node */
@NonNull
public abstract Iterable<ResolvedAnnotation> getAnnotations();
/**
* Searches for the annotation of the given type on this node
*
* @param type the fully qualified name of the annotation to check
* @return the annotation, or null if not found
*/
@Nullable
public ResolvedAnnotation getAnnotation(@NonNull String type) {
for (ResolvedAnnotation annotation : getAnnotations()) {
if (annotation.getType().matchesSignature(type)) {
return annotation;
}
}
return null;
}
}
/** A resolved class declaration (class, interface, enumeration or annotation) */
public abstract static class ResolvedClass extends ResolvedNode {
/** Returns the fully qualified name of this class */
@Override
@NonNull
public abstract String getName();
/** Returns the simple of this class */
@NonNull
public abstract String getSimpleName();
/** Returns the package name of this class */
@NonNull
public String getPackageName() {
String name = getName();
String simpleName = getSimpleName();
if (name.length() > simpleName.length() + 1) {
return name.substring(0, name.length() - simpleName.length() - 1);
}
return name;
}
/** Returns whether this class' fully qualified name matches the given name */
public abstract boolean matches(@NonNull String name);
@Nullable
public abstract ResolvedClass getSuperClass();
@Nullable
public abstract ResolvedClass getContainingClass();
public TypeDescriptor getType() {
return new DefaultTypeDescriptor(getName());
}
/**
* Determines whether this class extends the given name. If strict is true,
* it will not consider C extends C true.
*
* @param name the fully qualified class name
* @param strict if true, do not consider a class to be extending itself
* @return true if this class extends the given class
*/
public abstract boolean isSubclassOf(@NonNull String name, boolean strict);
@NonNull
public abstract Iterable<ResolvedMethod> getConstructors();
/** Returns the methods defined in this class, and optionally any methods inherited from any superclasses as well */
@NonNull
public abstract Iterable<ResolvedMethod> getMethods(boolean includeInherited);
/** Returns the methods of a given name defined in this class, and optionally any methods inherited from any superclasses as well */
@NonNull
public abstract Iterable<ResolvedMethod> getMethods(@NonNull String name, boolean includeInherited);
/** Returns the named field defined in this class, or optionally inherited from a superclass */
@Nullable
public abstract ResolvedField getField(@NonNull String name, boolean includeInherited);
}
/** A method or constructor declaration */
public abstract static class ResolvedMethod extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this method name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract ResolvedClass getContainingClass();
public abstract int getArgumentCount();
@NonNull
public abstract TypeDescriptor getArgumentType(int index);
/** Returns true if the parameter at the given index matches the given type signature */
public boolean argumentMatchesType(int index, @NonNull String signature) {
return getArgumentType(index).matchesSignature(signature);
}
@Nullable
public abstract TypeDescriptor getReturnType();
public boolean isConstructor() {
return getReturnType() == null;
}
/** Returns any annotations defined on the given parameter of this method */
@NonNull
public abstract Iterable<ResolvedAnnotation> getParameterAnnotations(int index);
/**
* Searches for the annotation of the given type on the method
*
* @param type the fully qualified name of the annotation to check
* @param parameterIndex the index of the parameter to look up
* @return the annotation, or null if not found
*/
@Nullable
public ResolvedAnnotation getParameterAnnotation(@NonNull String type,
int parameterIndex) {
for (ResolvedAnnotation annotation : getParameterAnnotations(parameterIndex)) {
if (annotation.getType().matchesSignature(type)) {
return annotation;
}
}
return null;
}
/** Returns the super implementation of the given method, if any */
@Nullable
public ResolvedMethod getSuperMethod() {
ResolvedClass cls = getContainingClass().getSuperClass();
if (cls != null) {
String methodName = getName();
int argCount = getArgumentCount();
for (ResolvedMethod method : cls.getMethods(methodName, true)) {
if (argCount != method.getArgumentCount()) {
continue;
}
boolean sameTypes = true;
for (int arg = 0; arg < argCount; arg++) {
if (!method.getArgumentType(arg).equals(getArgumentType(arg))) {
sameTypes = false;
break;
}
}
if (sameTypes) {
return method;
}
}
}
return null;
}
}
/** A field declaration */
public abstract static class ResolvedField extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this field name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract TypeDescriptor getType();
@NonNull
public abstract ResolvedClass getContainingClass();
@Nullable
public abstract Object getValue();
public String getContainingClassName() {
return getContainingClass().getName();
}
}
/**
* An annotation <b>reference</b>. Note that this refers to a usage of an annotation,
* not a declaraton of an annotation. You can call {@link #getClassType()} to
* find the declaration for the annotation.
*/
public abstract static class ResolvedAnnotation extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this field name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract TypeDescriptor getType();
/** Returns the {@link ResolvedClass} which defines the annotation */
@Nullable
public abstract ResolvedClass getClassType();
public static class Value {
@NonNull public final String name;
@Nullable public final Object value;
public Value(@NonNull String name, @Nullable Object value) {
this.name = name;
this.value = value;
}
}
@NonNull
public abstract List<Value> getValues();
@Nullable
public Object getValue(@NonNull String name) {
for (Value value : getValues()) {
if (name.equals(value.name)) {
return value.value;
}
}
return null;
}
@Nullable
public Object getValue() {
return getValue(ATTR_VALUE);
}
@NonNull
@Override
public Iterable<ResolvedAnnotation> getAnnotations() {
return Collections.emptyList();
}
}
/** A local variable or parameter declaration */
public abstract static class ResolvedVariable extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this variable name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract TypeDescriptor getType();
public void dispose(@NonNull JavaContext context, @NonNull UFile compilationUnit) {
}
}
@@ -14,16 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.CLASS_FOLDER;
import static com.android.SdkConstants.DOT_AAR;
import static com.android.SdkConstants.DOT_JAR;
import static com.android.SdkConstants.GEN_FOLDER;
import static com.android.SdkConstants.LIBS_FOLDER;
import static com.android.SdkConstants.RES_FOLDER;
import static com.android.SdkConstants.SRC_FOLDER;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
package com.android.tools.klint.client.api;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
@@ -35,21 +26,21 @@ import com.android.prefs.AndroidLocation;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkVersionInfo;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.utils.XmlUtils;
import com.google.common.annotations.Beta;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import org.jetbrains.uast.UastConverter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -59,13 +50,10 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static com.android.SdkConstants.*;
import static com.android.tools.klint.detector.api.LintUtils.endsWith;
/**
* Information about the tool embedding the lint analyzer. IDEs and other tools
@@ -153,19 +141,6 @@ public abstract class LintClient {
@Nullable
public abstract XmlParser getXmlParser();
/**
* Returns a {@link JavaParser} to use to parse Java
*
* @param project the project to parse, if known (this can be used to look up
* the class path for type attribution etc, and it can also be used
* to more efficiently process a set of files, for example to
* perform type attribution for multiple units in a single pass)
* @return a new {@link JavaParser}, or null if this client does not
* support Java analysis
*/
@Nullable
public abstract JavaParser getJavaParser(@Nullable Project project);
/**
* Returns an optimal detector, if applicable. By default, just returns the
* original detector, but tools can replace detectors using this hook with a version
@@ -406,6 +381,15 @@ public abstract class LintClient {
return false;
}
@Nullable
public com.intellij.openapi.project.Project getProject() {
return null;
}
public List<UastConverter> getConverters() {
return Collections.emptyList();
}
/**
* Information about class paths (sources, class files and libraries)
* usually associated with a project.
@@ -14,24 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ATTR_IGNORE;
import static com.android.SdkConstants.CLASS_CONSTRUCTOR;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.FD_GRADLE_WRAPPER;
import static com.android.SdkConstants.FN_GRADLE_WRAPPER_PROPERTIES;
import static com.android.SdkConstants.FN_LOCAL_PROPERTIES;
import static com.android.SdkConstants.RES_FOLDER;
import static com.android.SdkConstants.SUPPRESS_ALL;
import static com.android.SdkConstants.SUPPRESS_LINT;
import static com.android.SdkConstants.TOOLS_URI;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static com.android.tools.lint.detector.api.LintUtils.isAnonymousClass;
import static java.io.File.separator;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -40,79 +23,47 @@ import com.android.ide.common.res2.ResourceItem;
import com.android.resources.ResourceFolderType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.lint.client.api.LintListener.EventType;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.ResourceContext;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.JavaContext;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.ResourceContext;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.*;
import com.sun.istack.internal.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastChecker;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.java.JavaUastCallKinds;
import org.jetbrains.uast.visitor.UastVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.*;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
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.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.ast.Annotation;
import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.ArrayInitializer;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.MethodDeclaration;
import lombok.ast.Modifiers;
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.TypeReference;
import lombok.ast.VariableDefinition;
import static com.android.SdkConstants.*;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static java.io.File.separator;
/**
* Analyzes Android projects and files
@@ -415,7 +366,7 @@ public class LintDriver {
mScope = Scope.infer(projects);
}
fireEvent(EventType.STARTING, null);
fireEvent(LintListener.EventType.STARTING, null);
for (Project project : projects) {
mPhase = 1;
@@ -438,7 +389,7 @@ public class LintDriver {
runExtraPhases(project, main);
}
fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null);
fireEvent(mCanceled ? LintListener.EventType.CANCELED : LintListener.EventType.COMPLETED, null);
}
private void registerCustomRules(Collection<Project> projects) {
@@ -484,7 +435,7 @@ public class LintDriver {
do {
mPhase++;
fireEvent(EventType.NEW_PHASE,
fireEvent(LintListener.EventType.NEW_PHASE,
new Context(this, project, null, project.getDir()));
// Narrow the scope down to the set of scopes requested by
@@ -629,16 +580,16 @@ public class LintDriver {
assert detector instanceof Detector.XmlScanner : detector;
}
}
List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_JAVA_FILES);
List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_SOURCE_FILES);
if (javaCodeDetectors != null) {
for (Detector detector : javaCodeDetectors) {
assert detector instanceof Detector.JavaScanner : detector;
assert (detector instanceof UastScanner) : detector;
}
}
List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.JAVA_FILE);
List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.SOURCE_FILE);
if (javaFileDetectors != null) {
for (Detector detector : javaFileDetectors) {
assert detector instanceof Detector.JavaScanner : detector;
assert (detector instanceof UastScanner) : detector;
}
}
@@ -861,7 +812,7 @@ public class LintDriver {
File projectDir = project.getDir();
Context projectContext = new Context(this, project, null, projectDir);
fireEvent(EventType.SCANNING_PROJECT, projectContext);
fireEvent(LintListener.EventType.SCANNING_PROJECT, projectContext);
List<Project> allLibraries = project.getAllLibraries();
Set<Project> allProjects = new HashSet<Project>(allLibraries.size() + 1);
@@ -885,7 +836,7 @@ public class LintDriver {
List<Project> libraries = project.getAllLibraries();
for (Project library : libraries) {
Context libraryContext = new Context(this, library, project, projectDir);
fireEvent(EventType.SCANNING_LIBRARY_PROJECT, libraryContext);
fireEvent(LintListener.EventType.SCANNING_LIBRARY_PROJECT, libraryContext);
mCurrentProject = library;
for (Detector check : mApplicableDetectors) {
@@ -954,7 +905,7 @@ public class LintDriver {
if (detectors != null) {
ResourceVisitor v = new ResourceVisitor(parser, detectors,
null);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
v.visitFile(context, manifestFile);
}
}
@@ -1014,9 +965,9 @@ public class LintDriver {
}
}
if (mScope.contains(Scope.JAVA_FILE) || mScope.contains(Scope.ALL_JAVA_FILES)) {
List<Detector> checks = union(mScopeDetectors.get(Scope.JAVA_FILE),
mScopeDetectors.get(Scope.ALL_JAVA_FILES));
if (mScope.contains(Scope.SOURCE_FILE) || mScope.contains(Scope.ALL_SOURCE_FILES)) {
List<Detector> checks = union(mScopeDetectors.get(Scope.SOURCE_FILE),
mScopeDetectors.get(Scope.ALL_SOURCE_FILES));
if (checks != null && !checks.isEmpty()) {
List<File> files = project.getSubset();
if (files != null) {
@@ -1111,7 +1062,7 @@ public class LintDriver {
List<File> files = project.getProguardFiles();
for (File file : files) {
Context context = new Context(this, project, main, file);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector detector : detectors) {
if (detector.appliesTo(context, file)) {
detector.beforeCheckFile(context);
@@ -1137,7 +1088,7 @@ public class LintDriver {
File file = new File(project.getDir(), relativePath);
if (file.exists()) {
Context context = new Context(this, project, main, file);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector detector : detectors) {
if (detector.appliesTo(context, file)) {
detector.beforeCheckFile(context);
@@ -1476,12 +1427,6 @@ public class LintDriver {
@Nullable Project main,
@NonNull List<File> sourceFolders,
@NonNull List<Detector> checks) {
JavaParser javaParser = mClient.getJavaParser(project);
if (javaParser == null) {
mClient.log(null, "No java parser provided to lint: not running Java checks");
return;
}
assert !checks.isEmpty();
// Gather all Java source files in a single pass; more efficient.
@@ -1490,22 +1435,39 @@ public class LintDriver {
gatherJavaFiles(folder, sources);
}
if (!sources.isEmpty()) {
JavaVisitor visitor = new JavaVisitor(javaParser, checks);
List<JavaContext> contexts = Lists.newArrayListWithExpectedSize(sources.size());
for (File file : sources) {
JavaContext context = new JavaContext(this, project, main, file, javaParser);
contexts.add(context);
}
visitor.prepare(contexts);
com.intellij.openapi.project.Project ideaProject = mClient.getProject();
if (ideaProject == null) {
return;
}
List<UastConverter> converters = mClient.getConverters();
for (JavaContext context : contexts) {
fireEvent(EventType.SCANNING_FILE, context);
visitor.visitFile(context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector check : checks) {
if (check instanceof UastScanner) {
UastScanner scanner = (UastScanner) check;
UastVisitor customVisitor = scanner.createUastVisitor(context);
if (customVisitor != null) {
UastChecker.INSTANCE.checkWithCustomHandler(
ideaProject, context.file, converters, customVisitor);
} else {
UastChecker.INSTANCE.check(
ideaProject, context.file, (UastScanner)check, converters, context);
}
}
}
if (mCanceled) {
return;
}
}
visitor.dispose();
}
}
@@ -1514,41 +1476,52 @@ public class LintDriver {
@Nullable Project main,
@NonNull List<Detector> checks,
@NonNull List<File> files) {
List<UastScanner> uastDetectors = new ArrayList<UastScanner>();
JavaParser javaParser = mClient.getJavaParser(project);
if (javaParser == null) {
mClient.log(null, "No java parser provided to lint: not running Java checks");
for (Detector check : checks) {
if (check instanceof UastScanner) {
uastDetectors.add((UastScanner) check);
}
}
checkWithUastDetectors(project, main, uastDetectors, files);
}
private void checkWithUastDetectors(
@NotNull Project project,
@Nullable Project main,
@NotNull List<UastScanner> detectors,
@NotNull List<File> files) {
com.intellij.openapi.project.Project intellijProject = mClient.getProject();
if (intellijProject == null) {
return;
}
JavaVisitor visitor = new JavaVisitor(javaParser, checks);
UastChecker checker = UastChecker.INSTANCE;
List<UastConverter> converters = project.getClient().getConverters();
List<JavaContext> contexts = Lists.newArrayListWithExpectedSize(files.size());
for (File file : files) {
if (file.isFile() && file.getPath().endsWith(DOT_JAVA)) {
contexts.add(new JavaContext(this, project, main, file, javaParser));
if (!file.isFile()) {
continue;
}
String path = file.getPath();
if (!path.endsWith(DOT_JAVA) && !UastConverterUtils.isFileSupported(converters, path)) {
continue;
}
JavaContext
context = new JavaContext(this, project, main, file);
for (UastScanner detector : detectors) {
UastVisitor customHandler = detector.createUastVisitor(context);
if (customHandler != null) {
checker.checkWithCustomHandler(intellijProject, file, converters, customHandler);
} else {
checker.check(intellijProject, file, detector, converters, context);
}
}
}
if (contexts.isEmpty()) {
return;
}
visitor.prepare(contexts);
if (mCanceled) {
return;
}
for (JavaContext context : contexts) {
fireEvent(EventType.SCANNING_FILE, context);
visitor.visitFile(context);
if (mCanceled) {
return;
}
}
visitor.dispose();
}
private static void gatherJavaFiles(@NonNull File dir, @NonNull List<File> result) {
@@ -1666,7 +1639,7 @@ public class LintDriver {
if (dirChecks != null && !dirChecks.isEmpty()) {
ResourceContext context = new ResourceContext(this, project, main, dir, type);
String folderName = dir.getName();
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector check : dirChecks) {
if (check.appliesTo(type)) {
check.beforeCheckFile(context);
@@ -1693,11 +1666,11 @@ public class LintDriver {
if (LintUtils.isXmlFile(file)) {
XmlContext context = new XmlContext(this, project, main, file, type,
visitor.getParser());
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitFile(context, file);
} else if (binaryChecks != null && LintUtils.isBitmapFile(file)) {
ResourceContext context = new ResourceContext(this, project, main, file, type);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitBinaryResource(context);
}
if (mCanceled) {
@@ -1739,7 +1712,7 @@ public class LintDriver {
if (visitor != null) {
XmlContext context = new XmlContext(this, project, main, file, type,
visitor.getParser());
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitFile(context, file);
}
}
@@ -1752,7 +1725,7 @@ public class LintDriver {
if (visitor != null) {
ResourceContext context = new ResourceContext(this, project, main, file,
type);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitBinaryResource(context);
if (mCanceled) {
return;
@@ -1920,12 +1893,6 @@ public class LintDriver {
return mDelegate.getProject(dir, referenceDir);
}
@Nullable
@Override
public JavaParser getJavaParser(@Nullable Project project) {
return mDelegate.getJavaParser(project);
}
@Override
public File findResource(@NonNull String relativePath) {
return mDelegate.findResource(relativePath);
@@ -1939,7 +1906,7 @@ public class LintDriver {
@Override
@NonNull
protected ClassPathInfo getClassPath(@NonNull Project project) {
protected LintClient.ClassPathInfo getClassPath(@NonNull Project project) {
return mDelegate.getClassPath(project);
}
@@ -2069,6 +2036,17 @@ public class LintDriver {
public void closeConnection(@NonNull URLConnection connection) throws IOException {
mDelegate.closeConnection(connection);
}
@Override
public com.intellij.openapi.project.Project getProject() {
return mDelegate.getProject();
}
@Override
public List<UastConverter> getConverters() {
return mDelegate.getConverters();
}
}
/**
@@ -2140,7 +2118,7 @@ public class LintDriver {
return true;
}
} else if (classNode.outerClass != null && classNode.outerMethod == null
&& isAnonymousClass(classNode)) {
&& LintUtils.isAnonymousClass(classNode)) {
if (isSuppressed(issue, classNode)) {
return true;
}
@@ -2251,7 +2229,7 @@ public class LintDriver {
}
if (classNode.outerClass != null && classNode.outerMethod == null
&& isAnonymousClass(classNode)) {
&& LintUtils.isAnonymousClass(classNode)) {
ClassNode outer = getOuterClassNode(classNode);
if (outer != null) {
MethodNode m = findMethod(outer, CONSTRUCTOR_NAME, false);
@@ -2347,44 +2325,31 @@ public class LintDriver {
* issue in this class
*/
public boolean isSuppressed(@Nullable JavaContext context, @NonNull Issue issue,
@Nullable Node scope) {
@Nullable UElement scope) {
boolean checkComments = mClient.checkForSuppressComments() &&
context != null && context.containsCommentSuppress();
context != null && context.containsCommentSuppress();
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
// so no need to do instanceof stuff here.
if (type == VariableDefinition.class) {
// Variable
VariableDefinition declaration = (VariableDefinition) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
if (scope instanceof UVariable) {
UVariable declaration = (UVariable) scope;
if (isUastSuppressed(issue, declaration.getAnnotations())) {
return true;
}
} else if (type == MethodDeclaration.class) {
// Method
// Look for annotations on the method
MethodDeclaration declaration = (MethodDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
} else if (scope instanceof UFunction) {
UFunction declaration = (UFunction) scope;
if (isUastSuppressed(issue, declaration.getAnnotations())) {
return true;
}
} else if (type == ConstructorDeclaration.class) {
// Constructor
// Look for annotations on the method
ConstructorDeclaration declaration = (ConstructorDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
return true;
}
} else if (type == ClassDeclaration.class) {
// Class
ClassDeclaration declaration = (ClassDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
} else if (scope instanceof UClass) {
UClass declaration = (UClass) scope;
if (isUastSuppressed(issue, declaration.getAnnotations())) {
return true;
}
}
if (checkComments && context.isSuppressedWithComment(scope, issue)) {
return true;
}
//TODO check comments
//if (checkComments && context.isSuppressedWithComment(scope, issue)) {
// return true;
//}
scope = scope.getParent();
}
@@ -2401,47 +2366,27 @@ public class LintDriver {
* @return true if the issue or all issues should be suppressed for this
* modifier
*/
private static boolean isSuppressed(@Nullable Issue issue, @Nullable Modifiers modifiers) {
if (modifiers == null) {
return false;
}
StrictListAccessor<Annotation, Modifiers> annotations = modifiers.astAnnotations();
if (annotations == null) {
return false;
}
for (Annotation annotation : annotations) {
TypeReference t = annotation.astAnnotationTypeReference();
String typeName = t.getTypeName();
if (typeName.endsWith(SUPPRESS_LINT)
|| typeName.endsWith("SuppressWarnings")) { //$NON-NLS-1$
StrictListAccessor<AnnotationElement, Annotation> values =
annotation.astElements();
if (values != null) {
for (AnnotationElement element : values) {
AnnotationValue valueNode = element.astValue();
if (valueNode == null) {
continue;
private static boolean isUastSuppressed(@Nullable Issue issue, @Nullable List<UAnnotation> annotations) {
for (UAnnotation annotation : annotations) {
if (annotation.matchesName(SUPPRESS_LINT)
|| annotation.matchesName("SuppressWarnings")) { //$NON-NLS-1$
List<UNamedExpression> values = annotation.getValueArguments();
for (UNamedExpression element : values) {
UExpression valueNode = element.getExpression();
String value = UastLiteralUtils.getValueIfStringLiteral(valueNode);
if (value != null) {
if (matches(issue, value)) {
return true;
}
if (valueNode instanceof StringLiteral) {
StringLiteral literal = (StringLiteral) valueNode;
String value = literal.astValue();
if (matches(issue, value)) {
return true;
}
} else if (valueNode instanceof ArrayInitializer) {
ArrayInitializer array = (ArrayInitializer) valueNode;
StrictListAccessor<Expression, ArrayInitializer> expressions =
array.astExpressions();
if (expressions == null) {
continue;
}
for (Expression arrayElement : expressions) {
if (arrayElement instanceof StringLiteral) {
String value = ((StringLiteral) arrayElement).astValue();
if (matches(issue, value)) {
return true;
}
} else if (valueNode instanceof UCallExpression
&& ((UCallExpression)valueNode).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) {
UCallExpression array = (UCallExpression) valueNode;
List<UExpression> expressions = array.getValueArguments();
for (UExpression arrayElement : expressions) {
String elementValue = UastLiteralUtils.getValueIfStringLiteral(arrayElement);
if (elementValue != null) {
if (matches(issue, elementValue)) {
return true;
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.uast.UastConverter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class LintLanguageExtension {
public static final ExtensionPointName<LintLanguageExtension> EP_NAME =
ExtensionPointName.create("com.android.tools.lint.client.api.lintLanguageExtension");
@NonNull
public abstract UastConverter getConverter();
public static boolean isFileSupported(@Nullable Project project, String path) {
LintLanguageExtension[] extensions = getExtensions(project);
for (LintLanguageExtension ext : extensions) {
if (ext.getConverter().isFileSupported(path)) {
return true;
}
}
return false;
}
public static List<UastConverter> getConverters(@Nullable Project project) {
if (project == null) {
return Collections.emptyList();
}
LintLanguageExtension[] languageExtensions = project.getExtensions(EP_NAME);
List<UastConverter> converters = new ArrayList<UastConverter>(languageExtensions.length);
for (LintLanguageExtension extension : languageExtensions) {
converters.add(extension.getConverter());
}
return converters;
}
public static LintLanguageExtension[] getExtensions(@Nullable Project project) {
if (project == null) {
return new LintLanguageExtension[0];
}
return project.getExtensions(EP_NAME);
}
}
@@ -14,11 +14,11 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.klint.detector.api.Context;
import com.google.common.annotations.Beta;
/**
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Scope;
import com.google.common.annotations.Beta;
import java.io.File;
@@ -13,30 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.FD_ASSETS;
import static com.android.tools.lint.detector.api.Detector.OtherFileScanner;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Scope;
import com.android.utils.SdkUtils;
import com.google.common.collect.Lists;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import static com.android.SdkConstants.*;
import static com.android.tools.klint.detector.api.Detector.OtherFileScanner;
/**
* Visitor for "other" files: files that aren't java sources,
@@ -102,7 +94,7 @@ class OtherFileVisitor {
}
}
if (scopes.contains(Scope.JAVA_FILE)) {
if (scopes.contains(Scope.SOURCE_FILE)) {
if (subset != null && !subset.isEmpty()) {
List<File> files = new ArrayList<File>(subset.size());
for (File file : subset) {
@@ -111,7 +103,7 @@ class OtherFileVisitor {
}
}
if (!files.isEmpty()) {
mFiles.put(Scope.JAVA_FILE, files);
mFiles.put(Scope.SOURCE_FILE, files);
}
} else {
List<File> files = Lists.newArrayListWithExpectedSize(100);
@@ -119,7 +111,7 @@ class OtherFileVisitor {
collectFiles(files, srcFolder);
}
if (!files.isEmpty()) {
mFiles.put(Scope.JAVA_FILE, files);
mFiles.put(Scope.SOURCE_FILE, files);
}
}
}
@@ -14,30 +14,20 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.XmlScanner;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.ResourceContext;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Detector.XmlScanner;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.ResourceContext;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.*;
/**
* Specialized visitor for running detectors on resources: typically XML documents,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,15 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,34 +14,21 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD;
import static com.android.tools.lint.detector.api.Location.SearchDirection.EOL_BACKWARD;
import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.detector.api.Location.SearchDirection;
import com.android.tools.lint.detector.api.Location.SearchHints;
import com.android.tools.klint.client.api.LintDriver;
import com.google.common.annotations.Beta;
import com.google.common.base.Splitter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.*;
import java.io.File;
import java.util.List;
import static com.android.SdkConstants.*;
/**
* A {@link Context} used when checking .class files.
* <p/>
@@ -261,7 +248,7 @@ public class ClassContext extends Context {
*/
@NonNull
public Location getLocationForLine(int line, @Nullable String patternStart,
@Nullable String patternEnd, @Nullable SearchHints hints) {
@Nullable String patternEnd, @Nullable Location.SearchHints hints) {
File sourceFile = getSourceFile();
if (sourceFile != null) {
// ASM line numbers are 1-based, and lint line numbers are 0-based
@@ -515,7 +502,7 @@ public class ClassContext extends Context {
}
return getLocationForLine(findLineNumber(classNode), pattern, null,
SearchHints.create(BACKWARD).matchJavaSymbol());
Location.SearchHints.create(Location.SearchDirection.BACKWARD).matchJavaSymbol());
}
@Nullable
@@ -556,21 +543,21 @@ public class ClassContext extends Context {
// to find a method, look up the corresponding line number then search
// around it for a suitable tag, such as the class name.
String pattern;
SearchDirection searchMode;
Location.SearchDirection searchMode;
if (methodNode.name.equals(CONSTRUCTOR_NAME)) {
searchMode = EOL_BACKWARD;
searchMode = Location.SearchDirection.EOL_BACKWARD;
if (isAnonymousClass(classNode.name)) {
pattern = classNode.superName.substring(classNode.superName.lastIndexOf('/') + 1);
} else {
pattern = classNode.name.substring(classNode.name.lastIndexOf('$') + 1);
}
} else {
searchMode = BACKWARD;
searchMode = Location.SearchDirection.BACKWARD;
pattern = methodNode.name;
}
return getLocationForLine(findLineNumber(methodNode), pattern, null,
SearchHints.create(searchMode).matchJavaSymbol());
Location.SearchHints.create(searchMode).matchJavaSymbol());
}
/**
@@ -582,7 +569,7 @@ public class ClassContext extends Context {
*/
@NonNull
public Location getLocation(@NonNull AbstractInsnNode instruction) {
SearchHints hints = SearchHints.create(FORWARD).matchJavaSymbol();
Location.SearchHints hints = Location.SearchHints.create(Location.SearchDirection.FORWARD).matchJavaSymbol();
String pattern = null;
if (instruction instanceof MethodInsnNode) {
MethodInsnNode call = (MethodInsnNode) instruction;
@@ -14,19 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.DOT_GRADLE;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.SUPPRESS_ALL;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.client.api.SdkInfo;
import com.android.tools.klint.client.api.Configuration;
import com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.client.api.LintDriver;
import com.android.tools.klint.client.api.SdkInfo;
import com.google.common.annotations.Beta;
import java.io.File;
@@ -34,6 +29,8 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import static com.android.SdkConstants.*;
/**
* Context passed to the detectors during an analysis run. It provides
* information about the file being analyzed, it allows shared properties (so
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.google.common.annotations.Beta;
@@ -14,16 +14,18 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.LintDriver;
import com.google.common.annotations.Beta;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UFunction;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.visitor.UastVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
@@ -33,17 +35,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import lombok.ast.AstVisitor;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorInvocation;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import java.util.*;
/**
* A detector is able to find a particular problem (or a set of related problems).
@@ -70,192 +62,6 @@ import lombok.ast.Node;
*/
@Beta
public abstract class Detector {
/** Specialized interface for detectors that scan Java source file parse trees */
public interface JavaScanner {
/**
* Create a parse tree visitor to process the parse tree. All
* {@link JavaScanner} detectors must provide a visitor, unless they
* either return true from {@link #appliesToResourceRefs()} or return
* non null from {@link #getApplicableMethodNames()}.
* <p>
* If you return specific AST node types from
* {@link #getApplicableNodeTypes()}, then the visitor will <b>only</b>
* be called for the specific requested node types. This is more
* efficient, since it allows many detectors that apply to only a small
* part of the AST (such as method call nodes) to share iteration of the
* majority of the parse tree.
* <p>
* If you return null from {@link #getApplicableNodeTypes()}, then your
* visitor will be called from the top and all node types visited.
* <p>
* Note that a new visitor is created for each separate compilation
* unit, so you can store per file state in the visitor.
*
* @param context the {@link Context} for the file being analyzed
* @return a visitor, or null.
*/
@Nullable
AstVisitor createJavaVisitor(@NonNull JavaContext context);
/**
* Return the types of AST nodes that the visitor returned from
* {@link #createJavaVisitor(JavaContext)} should visit. See the
* documentation for {@link #createJavaVisitor(JavaContext)} for details
* on how the shared visitor is used.
* <p>
* If you return null from this method, then the visitor will process
* the full tree instead.
* <p>
* Note that for the shared visitor, the return codes from the visit
* methods are ignored: returning true will <b>not</b> prune iteration
* of the subtree, since there may be other node types interested in the
* children. If you need to ensure that your visitor only processes a
* part of the tree, use a full visitor instead. See the
* OverdrawDetector implementation for an example of this.
*
* @return the list of applicable node types (AST node classes), or null
*/
@Nullable
List<Class<? extends Node>> getApplicableNodeTypes();
/**
* Return the list of method names this detector is interested in, or
* null. If this method returns non-null, then any AST nodes that match
* a method call in the list will be passed to the
* {@link #visitMethod(JavaContext, AstVisitor, MethodInvocation)}
* method for processing. The visitor created by
* {@link #createJavaVisitor(JavaContext)} is also passed to that
* method, although it can be null.
* <p>
* This makes it easy to write detectors that focus on some fixed calls.
* For example, the StringFormatDetector uses this mechanism to look for
* "format" calls, and when found it looks around (using the AST's
* {@link Node#getParent()} method) to see if it's called on
* a String class instance, and if so do its normal processing. Note
* that since it doesn't need to do any other AST processing, that
* detector does not actually supply a visitor.
*
* @return a set of applicable method names, or null.
*/
@Nullable
List<String> getApplicableMethodNames();
/**
* Method invoked for any method calls found that matches any names
* returned by {@link #getApplicableMethodNames()}. This also passes
* back the visitor that was created by
* {@link #createJavaVisitor(JavaContext)}, but a visitor is not
* required. It is intended for detectors that need to do additional AST
* processing, but also want the convenience of not having to look for
* method names on their own.
*
* @param context the context of the lint request
* @param visitor the visitor created from
* {@link #createJavaVisitor(JavaContext)}, or null
* @param node the {@link MethodInvocation} node for the invoked method
*/
void visitMethod(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull MethodInvocation node);
/**
* Return the list of constructor types this detector is interested in, or
* null. If this method returns non-null, then any AST nodes that match
* a constructor call in the list will be passed to the
* {@link #visitConstructor(JavaContext, AstVisitor, ConstructorInvocation, ResolvedMethod)}
* method for processing. The visitor created by
* {@link #createJavaVisitor(JavaContext)} is also passed to that
* method, although it can be null.
* <p>
* This makes it easy to write detectors that focus on some fixed constructors.
*
* @return a set of applicable fully qualified types, or null.
*/
@Nullable
List<String> getApplicableConstructorTypes();
/**
* Method invoked for any constructor calls found that matches any names
* returned by {@link #getApplicableConstructorTypes()}. This also passes
* back the visitor that was created by
* {@link #createJavaVisitor(JavaContext)}, but a visitor is not
* required. It is intended for detectors that need to do additional AST
* processing, but also want the convenience of not having to look for
* method names on their own.
*
* @param context the context of the lint request
* @param visitor the visitor created from
* {@link #createJavaVisitor(JavaContext)}, or null
* @param node the {@link ConstructorInvocation} node for the invoked method
* @param constructor the resolved constructor method with type information
*/
void visitConstructor(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull ConstructorInvocation node,
@NonNull ResolvedMethod constructor);
/**
* Returns whether this detector cares about Android resource references
* (such as {@code R.layout.main} or {@code R.string.app_name}). If it
* does, then the visitor will look for these patterns, and if found, it
* will invoke {@link #visitResourceReference} passing the resource type
* and resource name. It also passes the visitor, if any, that was
* created by {@link #createJavaVisitor(JavaContext)}, such that a
* detector can do more than just look for resources.
*
* @return true if this detector wants to be notified of R resource
* identifiers found in the code.
*/
boolean appliesToResourceRefs();
/**
* Called for any resource references (such as {@code R.layout.main}
* found in Java code, provided this detector returned {@code true} from
* {@link #appliesToResourceRefs()}.
*
* @param context the lint scanning context
* @param visitor the visitor created from
* {@link #createJavaVisitor(JavaContext)}, or null
* @param node the variable reference for the resource
* @param type the resource type, such as "layout" or "string"
* @param name the resource name, such as "main" from
* {@code R.layout.main}
* @param isFramework whether the resource is a framework resource
* (android.R) or a local project resource (R)
*/
void visitResourceReference(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull Node node,
@NonNull String type,
@NonNull String name,
boolean isFramework);
/**
* Returns a list of fully qualified names for super classes that this
* detector cares about. If not null, this detector will *only* be called
* if the current class is a subclass of one of the specified superclasses.
*
* @return a list of fully qualified names
*/
@Nullable
List<String> applicableSuperClasses();
/**
* Called for each class that extends one of the super classes specified with
* {@link #applicableSuperClasses()}
*
* @param context the lint scanning context
* @param declaration the class declaration node, or null for anonymous classes
* @param node the class declaration node or the anonymous class construction node
* @param resolvedClass the resolved class
*/
void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
@NonNull Node node, @NonNull ResolvedClass resolvedClass);
}
/** Specialized interface for detectors that scan Java class files */
public interface ClassScanner {
/**
@@ -664,52 +470,48 @@ public abstract class Detector {
return null;
}
@Nullable @SuppressWarnings("javadoc")
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
return null;
}
@Nullable @SuppressWarnings("javadoc")
public List<Class<? extends Node>> getApplicableNodeTypes() {
return null;
}
@SuppressWarnings("javadoc")
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
}
@SuppressWarnings("javadoc")
public boolean appliesToResourceRefs() {
return false;
}
@SuppressWarnings("javadoc")
public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull Node node, @NonNull String type, @NonNull String name,
boolean isFramework) {
}
@Nullable
public List<String> applicableSuperClasses() {
return null;
}
public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
@NonNull Node node, @NonNull ResolvedClass resolvedClass) {
}
public void visitResourceReference(
UastAndroidContext context,
UElement element,
String type,
String name,
boolean isFramework
) {}
@Nullable @SuppressWarnings("javadoc")
public List<String> getApplicableConstructorTypes() {
return null;
}
@SuppressWarnings("javadoc")
public void visitConstructor(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull ConstructorInvocation node,
@NonNull ResolvedMethod constructor) {
public List<String> getApplicableSuperClasses() {
return null;
}
public void visitClass(UastAndroidContext context, UClass node) {
}
public List<String> getApplicableFunctionNames() {
return null;
}
public void visitConstructor(UastAndroidContext context, UCallExpression functionCall,
UFunction constructor) {
}
public UastVisitor createUastVisitor(UastAndroidContext context) {
return null;
}
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
}
// ---- Dummy implementations to make implementing a ClassScanner easier: ----
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.google.common.annotations.Beta;
@@ -14,12 +14,9 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.tools.lint.detector.api.TextFormat.RAW;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.Configuration;
import com.google.common.annotations.Beta;
import java.util.ArrayList;
@@ -142,7 +139,7 @@ public final class Issue implements Comparable<Issue> {
*/
@NonNull
public String getBriefDescription(@NonNull TextFormat format) {
return RAW.convertTo(mBriefDescription, format);
return TextFormat.RAW.convertTo(mBriefDescription, format);
}
/**
@@ -156,7 +153,7 @@ public final class Issue implements Comparable<Issue> {
*/
@NonNull
public String getExplanation(@NonNull TextFormat format) {
return RAW.convertTo(mExplanation, format);
return TextFormat.RAW.convertTo(mExplanation, format);
}
/**
@@ -14,28 +14,19 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.CLASS_CONTEXT;
import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import static com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.klint.client.api.LintDriver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Position;
import static com.android.SdkConstants.CLASS_CONTEXT;
/**
* A {@link Context} used when checking Java files.
@@ -43,14 +34,11 @@ import lombok.ast.Position;
* <b>NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.</b>
*/
public class JavaContext extends Context {
public class JavaContext extends Context implements UastAndroidContext {
static final String SUPPRESS_COMMENT_PREFIX = "//noinspection "; //$NON-NLS-1$
/** The parse tree */
private Node mCompilationUnit;
/** The parser which produced the parse tree */
private final JavaParser mParser;
private UFile mCompilationUnit;
/**
* Constructs a {@link JavaContext} for running lint on the given file, with
@@ -70,10 +58,8 @@ public class JavaContext extends Context {
@NonNull LintDriver driver,
@NonNull Project project,
@Nullable Project main,
@NonNull File file,
@NonNull JavaParser parser) {
@NonNull File file) {
super(driver, project, main, file);
mParser = parser;
}
/**
@@ -82,18 +68,13 @@ public class JavaContext extends Context {
* @param node the AST node to get a location for
* @return a location for the given node
*/
@NonNull
public Location getLocation(@NonNull Node node) {
return mParser.getLocation(this, node);
}
@NonNull
public JavaParser getParser() {
return mParser;
@Override
public Location getLocation(@NotNull UElement element) {
return UastAndroidContext.DefaultImpls.getLocation(this, element);
}
@Nullable
public Node getCompilationUnit() {
public UFile getCompilationUnit() {
return mCompilationUnit;
}
@@ -103,7 +84,7 @@ public class JavaContext extends Context {
*
* @param compilationUnit the parse tree
*/
public void setCompilationUnit(@Nullable Node compilationUnit) {
public void setCompilationUnit(@Nullable UFile compilationUnit) {
mCompilationUnit = compilationUnit;
}
@@ -127,107 +108,24 @@ public class JavaContext extends Context {
* @param location the location of the issue, or null if not known
* @param message the message for this warning
*/
@Override
public void report(
@NonNull Issue issue,
@Nullable Node scope,
@Nullable Location location,
@NonNull String message) {
@NonNull Issue issue,
@Nullable UElement scope,
@Nullable Location location,
@NonNull String message) {
if (scope != null && mDriver.isSuppressed(this, issue, scope)) {
return;
}
super.report(issue, location, message);
}
/**
* Report an error.
* Like {@link #report(Issue, Node, Location, String)} but with
* a now-unused data parameter at the end.
*
* @deprecated Use {@link #report(Issue, Node, Location, String)} instead;
* this method is here for custom rule compatibility
*/
@SuppressWarnings("UnusedDeclaration") // Potentially used by external existing custom rules
@Deprecated
public void report(
@NonNull Issue issue,
@Nullable Node scope,
@Nullable Location location,
@NonNull String message,
@SuppressWarnings("UnusedParameters") @Nullable Object data) {
report(issue, scope, location, message);
}
@Nullable
public static Node findSurroundingMethod(Node scope) {
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
// so no need to do instanceof stuff here.
if (type == MethodDeclaration.class || type == ConstructorDeclaration.class) {
return scope;
}
scope = scope.getParent();
}
return null;
}
@Nullable
public static ClassDeclaration findSurroundingClass(@Nullable Node scope) {
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
// so no need to do instanceof stuff here.
if (type == ClassDeclaration.class) {
return (ClassDeclaration) scope;
}
scope = scope.getParent();
}
return null;
}
@Override
@Nullable
protected String getSuppressCommentPrefix() {
return SUPPRESS_COMMENT_PREFIX;
}
public boolean isSuppressedWithComment(@NonNull Node scope, @NonNull Issue issue) {
// Check whether there is a comment marker
String contents = getContents();
assert contents != null; // otherwise we wouldn't be here
Position position = scope.getPosition();
if (position == null) {
return false;
}
int start = position.getStart();
return isSuppressedWithComment(start, issue);
}
@NonNull
public Location.Handle createLocationHandle(@NonNull Node node) {
return mParser.createLocationHandle(this, node);
}
@Nullable
public ResolvedNode resolve(@NonNull Node node) {
return mParser.resolve(this, node);
}
@Nullable
public ResolvedClass findClass(@NonNull String fullyQualifiedName) {
return mParser.findClass(this, fullyQualifiedName);
}
@Nullable
public TypeDescriptor getType(@NonNull Node node) {
return mParser.getType(this, node);
}
/**
* Returns true if the given method invocation node corresponds to a call on a
* {@code android.content.Context}
@@ -235,160 +133,35 @@ public class JavaContext extends Context {
* @param node the method call node
* @return true iff the method call is on a class extending context
*/
public boolean isContextMethod(@NonNull MethodInvocation node) {
public boolean isContextMethod(@NonNull UCallExpression node) {
// Method name used in many other contexts where it doesn't have the
// same semantics; only use this one if we can resolve types
// and we're certain this is the Context method
ResolvedNode resolved = resolve(node);
if (resolved instanceof JavaParser.ResolvedMethod) {
JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) resolved;
ResolvedClass containingClass = method.getContainingClass();
if (containingClass.isSubclassOf(CLASS_CONTEXT, false)) {
UFunction resolved = node.resolve(this);
UClass containingClass = UastUtils.getContainingClass(resolved);
if (resolved != null && containingClass != null) {
if (containingClass.isSubclassOf(CLASS_CONTEXT)) {
return true;
}
}
return false;
}
/**
* Returns the first ancestor node of the given type
*
* @param element the element to search from
* @param clz the target node type
* @param <T> the target node type
* @return the nearest ancestor node in the parent chain, or null if not found
*/
@Nullable
public static <T extends Node> T getParentOfType(
@Nullable Node element,
@NonNull Class<T> clz) {
return getParentOfType(element, clz, true);
@NotNull
@Override
public JavaContext getLintContext() {
return this;
}
/**
* Returns the first ancestor node of the given type
*
* @param element the element to search from
* @param clz the target node type
* @param strict if true, do not consider the element itself, only its parents
* @param <T> the target node type
* @return the nearest ancestor node in the parent chain, or null if not found
*/
@Nullable
public static <T extends Node> T getParentOfType(
@Nullable Node element,
@NonNull Class<T> clz,
boolean strict) {
if (element == null) {
return null;
}
if (strict) {
element = element.getParent();
}
while (element != null) {
if (clz.isInstance(element)) {
//noinspection unchecked
return (T) element;
}
element = element.getParent();
}
return null;
@NotNull
@Override
public List<UastConverter> getConverters() {
return getClient().getConverters();
}
/**
* Returns the first ancestor node of the given type, stopping at the given type
*
* @param element the element to search from
* @param clz the target node type
* @param strict if true, do not consider the element itself, only its parents
* @param terminators optional node types to terminate the search at
* @param <T> the target node type
* @return the nearest ancestor node in the parent chain, or null if not found
*/
@Nullable
public static <T extends Node> T getParentOfType(@Nullable Node element,
@NonNull Class<T> clz,
boolean strict,
@NonNull Class<? extends Node>... terminators) {
if (element == null) {
return null;
}
if (strict) {
element = element.getParent();
}
while (element != null && !clz.isInstance(element)) {
for (Class<?> terminator : terminators) {
if (terminator.isInstance(element)) {
return null;
}
}
element = element.getParent();
}
//noinspection unchecked
return (T) element;
}
/**
* Returns the first sibling of the given node that is of the given class
*
* @param sibling the sibling to search from
* @param clz the type to look for
* @param <T> the type
* @return the first sibling of the given type, or null
*/
@Nullable
public static <T extends Node> T getNextSiblingOfType(@Nullable Node sibling,
@NonNull Class<T> clz) {
if (sibling == null) {
return null;
}
Node parent = sibling.getParent();
if (parent == null) {
return null;
}
Iterator<Node> iterator = parent.getChildren().iterator();
while (iterator.hasNext()) {
if (iterator.next() == sibling) {
break;
}
}
while (iterator.hasNext()) {
Node child = iterator.next();
if (clz.isInstance(child)) {
//noinspection unchecked
return (T) child;
}
}
return null;
}
/**
* Returns the given argument of the given call
*
* @param call the call containing arguments
* @param index the index of the target argument
* @return the argument at the given index
* @throws IllegalArgumentException if index is outside the valid range
*/
@NonNull
public static Node getArgumentNode(@NonNull MethodInvocation call, int index) {
int i = 0;
for (Expression parameter : call.astArguments()) {
if (i == index) {
return parameter;
}
i++;
}
throw new IllegalArgumentException(Integer.toString(index));
@org.jetbrains.annotations.Nullable
@Override
public UElement convert(@Nullable Object element) {
return UastContext.DefaultImpls.convert(this, element);
}
}
@@ -14,25 +14,15 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
import static com.android.SdkConstants.ATTR_PADDING;
import static com.android.SdkConstants.ATTR_PADDING_BOTTOM;
import static com.android.SdkConstants.ATTR_PADDING_LEFT;
import static com.android.SdkConstants.ATTR_PADDING_RIGHT;
import static com.android.SdkConstants.ATTR_PADDING_TOP;
import static com.android.SdkConstants.VALUE_FILL_PARENT;
import static com.android.SdkConstants.VALUE_MATCH_PARENT;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.google.common.annotations.Beta;
import org.w3c.dom.Element;
import static com.android.SdkConstants.*;
/**
* Abstract class specifically intended for layout detectors which provides some
* common utility methods shared by layout detectors.
@@ -14,25 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.ANDROID_PREFIX;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_LOCALE;
import static com.android.SdkConstants.BIN_FOLDER;
import static com.android.SdkConstants.DOT_GIF;
import static com.android.SdkConstants.DOT_JPEG;
import static com.android.SdkConstants.DOT_JPG;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.SdkConstants.DOT_WEBP;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.ID_PREFIX;
import static com.android.SdkConstants.NEW_ID_PREFIX;
import static com.android.SdkConstants.TOOLS_URI;
import static com.android.SdkConstants.UTF_8;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static com.android.ide.common.resources.configuration.LocaleQualifier.BCP_47_PREFIX;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -53,7 +35,7 @@ import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkVersionInfo;
import com.android.sdklib.repository.FullRevision;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.klint.client.api.LintClient;
import com.android.utils.PositionXmlParser;
import com.android.utils.SdkUtils;
import com.google.common.annotations.Beta;
@@ -61,7 +43,8 @@ import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.jetbrains.uast.UFile;
import org.jetbrains.uast.UImportStatement;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
@@ -74,19 +57,14 @@ import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import lombok.ast.ImportDeclaration;
import static com.android.SdkConstants.*;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static com.android.ide.common.resources.configuration.LocaleQualifier.BCP_47_PREFIX;
/**
* Useful utility methods related to lint.
@@ -796,7 +774,7 @@ public class LintUtils {
* qualified name
*/
public static boolean isImported(
@Nullable lombok.ast.Node compilationUnit,
@Nullable UFile compilationUnit,
@NonNull String fullyQualifiedName) {
if (compilationUnit == null) {
return false;
@@ -805,26 +783,27 @@ public class LintUtils {
int dotLength = fullyQualifiedName.length() - dotIndex;
boolean imported = false;
for (lombok.ast.Node rootNode : compilationUnit.getChildren()) {
if (rootNode instanceof ImportDeclaration) {
ImportDeclaration importDeclaration = (ImportDeclaration) rootNode;
String fqn = importDeclaration.asFullyQualifiedName();
if (fqn.equals(fullyQualifiedName)) {
return true;
} else if (fullyQualifiedName.regionMatches(dotIndex, fqn,
fqn.length() - dotLength, dotLength)) {
// This import is importing the class name using some other prefix, so there
// fully qualified class name cannot be imported under that name
return false;
} else if (importDeclaration.astStarImport()
&& fqn.regionMatches(0, fqn, 0, dotIndex + 1)) {
imported = true;
// but don't break -- keep searching in case there's a non-wildcard
// import of the specific class name, e.g. if we're looking for
// android.content.SharedPreferences.Editor, don't match on the following:
// import android.content.SharedPreferences.*;
// import foo.bar.Editor;
}
for (UImportStatement importStatement : compilationUnit.getImportStatements()) {
String fqn = importStatement.getNameToImport();
if (fqn == null) {
continue;
}
if (fqn.equals(fullyQualifiedName)) {
return true;
} else if (fullyQualifiedName.regionMatches(dotIndex, fqn,
fqn.length() - dotLength, dotLength)) {
// This import is importing the class name using some other prefix, so there
// fully qualified class name cannot be imported under that name
return false;
} else if (importStatement.isStarImport()
&& fqn.regionMatches(0, fqn, 0, dotIndex + 1)) {
imported = true;
// but don't break -- keep searching in case there's a non-wildcard
// import of the specific class name, e.g. if we're looking for
// android.content.SharedPreferences.Editor, don't match on the following:
// import android.content.SharedPreferences.*;
// import foo.bar.Editor;
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.google.common.annotations.Beta;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
@@ -28,10 +28,10 @@ import com.android.resources.ResourceFolderType;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkVersionInfo;
import com.android.tools.lint.client.api.CircularDependencyException;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.SdkInfo;
import com.android.tools.klint.client.api.CircularDependencyException;
import com.android.tools.klint.client.api.Configuration;
import com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.client.api.SdkInfo;
import com.google.common.annotations.Beta;
import com.google.common.base.CharMatcher;
import com.google.common.base.Charsets;
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.klint.client.api.LintDriver;
import com.google.common.annotations.Beta;
import java.io.File;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
@@ -14,27 +14,19 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_GRADLE;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.SdkConstants.DOT_PROPERTIES;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.FN_PROJECT_PROGUARD_FILE;
import static com.android.SdkConstants.OLD_PROGUARD_FILE;
import static com.android.SdkConstants.RES_FOLDER;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.google.common.annotations.Beta;
import org.jetbrains.uast.UastConverterUtils;
import java.io.File;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import static com.android.SdkConstants.*;
/**
* The scope of a detector is the set of files a detector must consider when
* performing its analysis. This can be used to determine when issues are
@@ -80,14 +72,14 @@ public enum Scope {
* Issues which are only affected by a single Java source file can be
* checked for incrementally when a Java source file is edited.
*/
JAVA_FILE,
SOURCE_FILE,
/**
* The analysis considers <b>all</b> the Java source files together.
* <p>
* This flag is mutually exclusive with {@link #JAVA_FILE}.
* This flag is mutually exclusive with {@link #SOURCE_FILE}.
*/
ALL_JAVA_FILES,
ALL_SOURCE_FILES,
/**
* The analysis only considers a single Java class file at a time.
@@ -143,10 +135,10 @@ public enum Scope {
if (size == 2) {
// When single checking a Java source file, we check both its Java source
// and the associated class files
return scopes.contains(JAVA_FILE) && scopes.contains(CLASS_FILE);
return scopes.contains(SOURCE_FILE) && scopes.contains(CLASS_FILE);
} else {
return size == 1 &&
(scopes.contains(JAVA_FILE)
(scopes.contains(SOURCE_FILE)
|| scopes.contains(CLASS_FILE)
|| scopes.contains(RESOURCE_FILE)
|| scopes.contains(PROGUARD_FILE)
@@ -192,7 +184,7 @@ public enum Scope {
} else if (name.endsWith(DOT_XML)) {
scope.add(RESOURCE_FILE);
} else if (name.endsWith(DOT_JAVA)) {
scope.add(JAVA_FILE);
scope.add(SOURCE_FILE);
} else if (name.endsWith(DOT_CLASS)) {
scope.add(CLASS_FILE);
} else if (name.endsWith(DOT_GRADLE)) {
@@ -210,6 +202,9 @@ public enum Scope {
scope.add(RESOURCE_FILE);
scope.add(BINARY_RESOURCE_FILE);
scope.add(RESOURCE_FOLDER);
} else if (UastConverterUtils.isFileSupported(
project.getClient().getConverters(), name)) {
scope.add(SOURCE_FILE);
}
}
} else {
@@ -231,7 +226,7 @@ public enum Scope {
/** Scope-set used for detectors which scan all resources */
public static final EnumSet<Scope> ALL_RESOURCES_SCOPE = EnumSet.of(ALL_RESOURCE_FILES);
/** Scope-set used for detectors which are affected by a single Java source file */
public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(JAVA_FILE);
public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(SOURCE_FILE);
/** Scope-set used for detectors which are affected by a single Java class file */
public static final EnumSet<Scope> CLASS_FILE_SCOPE = EnumSet.of(CLASS_FILE);
/** Scope-set used for detectors which are affected by a single Gradle build file */
@@ -248,8 +243,8 @@ public enum Scope {
public static final EnumSet<Scope> MANIFEST_AND_RESOURCE_SCOPE =
EnumSet.of(Scope.MANIFEST, Scope.RESOURCE_FILE);
/** Scope-set used for detectors which are affected by single XML and Java source files */
public static final EnumSet<Scope> JAVA_AND_RESOURCE_FILES =
EnumSet.of(RESOURCE_FILE, JAVA_FILE);
public static final EnumSet<Scope> SOURCE_AND_RESOURCE_FILES =
EnumSet.of(RESOURCE_FILE, SOURCE_FILE);
/** Scope-set used for analyzing individual class files and all resource files */
public static final EnumSet<Scope> CLASS_AND_ALL_RESOURCE_FILES =
EnumSet.of(ALL_RESOURCE_FILES, CLASS_FILE);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.google.common.annotations.Beta;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.utils.SdkUtils;
@@ -14,15 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.client.api.XmlParser;
import com.android.tools.klint.client.api.LintDriver;
import com.android.tools.klint.client.api.XmlParser;
import com.google.common.annotations.Beta;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;