Lint: Rewrite Lint diagnostics using Uast.
This commit is contained in:
@@ -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);
|
||||
|
||||
+3
-3
@@ -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> {
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
+13
-29
@@ -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
|
||||
|
||||
+3
-3
@@ -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;
|
||||
|
||||
|
||||
+27
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
@@ -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;
|
||||
|
||||
+12
-20
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user