Replace UastConverter with UastLanguagePlugin, allow additional checkers

This commit is contained in:
Yan Zhulanow
2016-03-10 16:25:18 +03:00
parent 4ef6016ee8
commit d1939d52bc
16 changed files with 105 additions and 68 deletions
@@ -16,12 +16,23 @@
package com.android.tools.klint.client.api;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UastAdditionalChecker;
import org.jetbrains.uast.UastConverter;
import org.jetbrains.uast.java.JavaConverter;
import org.jetbrains.uast.java.JavaUastLanguagePlugin;
import java.util.List;
public class JavaLintLanguageExtension extends LintLanguageExtension {
@NotNull
@Override
public UastConverter getConverter() {
return JavaConverter.INSTANCE;
return JavaUastLanguagePlugin.INSTANCE.getConverter();
}
@NotNull
@Override
public List<UastAdditionalChecker> getAdditionalCheckers() {
return JavaUastLanguagePlugin.INSTANCE.getAdditionalCheckers();
}
}
@@ -40,7 +40,7 @@ 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.jetbrains.uast.UastLanguagePlugin;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -386,7 +386,7 @@ public abstract class LintClient {
return null;
}
public List<UastConverter> getConverters() {
public List<UastLanguagePlugin> getLanguagePlugins() {
return Collections.emptyList();
}
@@ -1445,7 +1445,7 @@ public class LintDriver {
if (ideaProject == null) {
return;
}
List<UastConverter> converters = mClient.getConverters();
List<UastLanguagePlugin> plugins = mClient.getLanguagePlugins();
for (JavaContext context : contexts) {
fireEvent(LintListener.EventType.SCANNING_FILE, context);
@@ -1456,10 +1456,10 @@ public class LintDriver {
UastVisitor customVisitor = scanner.createUastVisitor(context);
if (customVisitor != null) {
UastChecker.INSTANCE.checkWithCustomHandler(
ideaProject, context.file, converters, customVisitor);
ideaProject, context.file, plugins, customVisitor);
} else {
UastChecker.INSTANCE.check(
ideaProject, context.file, (UastScanner)check, converters, context);
ideaProject, context.file, (UastScanner)check, plugins, context);
}
}
}
@@ -1498,7 +1498,7 @@ public class LintDriver {
}
UastChecker checker = UastChecker.INSTANCE;
List<UastConverter> converters = project.getClient().getConverters();
List<UastLanguagePlugin> plugins = project.getClient().getLanguagePlugins();
for (File file : files) {
if (!file.isFile()) {
@@ -1506,7 +1506,7 @@ public class LintDriver {
}
String path = file.getPath();
if (!path.endsWith(DOT_JAVA) && !UastConverterUtils.isFileSupported(converters, path)) {
if (!path.endsWith(DOT_JAVA) && !UastConverterUtils.isFileSupported(plugins, path)) {
continue;
}
@@ -1516,9 +1516,9 @@ public class LintDriver {
for (UastScanner detector : detectors) {
UastVisitor customHandler = detector.createUastVisitor(context);
if (customHandler != null) {
checker.checkWithCustomHandler(intellijProject, file, converters, customHandler);
checker.checkWithCustomHandler(intellijProject, file, plugins, customHandler);
} else {
checker.check(intellijProject, file, detector, converters, context);
checker.check(intellijProject, file, detector, plugins, context);
}
}
}
@@ -2043,9 +2043,8 @@ public class LintDriver {
}
@Override
public List<UastConverter> getConverters() {
return mDelegate.getConverters();
public List<UastLanguagePlugin> getLanguagePlugins() {
return mDelegate.getLanguagePlugins();
}
}
@@ -16,22 +16,18 @@
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 org.jetbrains.uast.UastLanguagePlugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class LintLanguageExtension {
public abstract class LintLanguageExtension implements UastLanguagePlugin {
public static final ExtensionPointName<LintLanguageExtension> EP_NAME =
ExtensionPointName.create("com.android.tools.lint.client.api.lintLanguageExtension");
@NonNull
public abstract UastConverter getConverter();
ExtensionPointName.create("com.android.tools.klint.client.api.lintLanguageExtension");
public static boolean isFileSupported(@Nullable Project project, String path) {
LintLanguageExtension[] extensions = getExtensions(project);
@@ -44,16 +40,14 @@ public abstract class LintLanguageExtension {
return false;
}
public static List<UastConverter> getConverters(@Nullable Project project) {
public static List<UastLanguagePlugin> getPlugins(@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());
}
List<UastLanguagePlugin> converters = new ArrayList<UastLanguagePlugin>(languageExtensions.length);
Collections.addAll(converters, languageExtensions);
return converters;
}
@@ -155,8 +155,8 @@ public class JavaContext extends Context implements UastAndroidContext {
@NotNull
@Override
public List<UastConverter> getConverters() {
return getClient().getConverters();
public List<UastLanguagePlugin> getLanguagePlugins() {
return getClient().getLanguagePlugins();
}
@org.jetbrains.annotations.Nullable
@@ -203,7 +203,7 @@ public enum Scope {
scope.add(BINARY_RESOURCE_FILE);
scope.add(RESOURCE_FOLDER);
} else if (UastConverterUtils.isFileSupported(
project.getClient().getConverters(), name)) {
project.getClient().getLanguagePlugins(), name)) {
scope.add(SOURCE_FILE);
}
}
@@ -39,7 +39,7 @@ import com.intellij.psi.PsiElementFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.java.JavaConverter;
import org.jetbrains.uast.java.JavaUastLanguagePlugin;
import org.jetbrains.uast.visitor.UastVisitor;
/**
@@ -581,11 +581,12 @@ public abstract class PermissionRequirement {
PsiElementFactory factory = JavaPsiFacade.getInstance(
context.getClient().getProject()).getElementFactory();
UElement node = JavaConverter.INSTANCE.convertWithParent(factory.createClassFromText(
"class Test { void test() {\n"
+ "boolean result = " + value
+ ";\n}\n}"
, null).getContainingFile());
UElement node = JavaUastLanguagePlugin.INSTANCE.getConverter().
convertWithParent(factory.createClassFromText(
"class Test { void test() {\n"
+ "boolean result = " + value
+ ";\n}\n}"
, null).getContainingFile());
if (node != null) {
final AtomicReference<UExpression> reference = new AtomicReference<UExpression>();
@@ -148,7 +148,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator<State, State
} else if (fileType == StdFileTypes.PROPERTIES) {
scope = Scope.PROPERTY_SCOPE;
} else {
if (UastConverterUtils.isFileSupported(client.getConverters(), mainFile.getPath())) {
if (UastConverterUtils.isFileSupported(client.getLanguagePlugins(), mainFile.getPath())) {
scope = Scope.JAVA_FILE_SCOPE;
} else {
// #collectionInformation above should have prevented this
@@ -63,7 +63,7 @@ import org.jetbrains.android.sdk.AndroidSdkData;
import org.jetbrains.android.sdk.AndroidSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.uast.UastConverter;
import org.jetbrains.uast.UastLanguagePlugin;
import java.io.File;
import java.io.IOException;
@@ -87,11 +87,11 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC
@Nullable protected Map<com.android.tools.klint.detector.api.Project, Module> myModuleMap;
@NonNull
private List<UastConverter> myConverters;
private List<UastLanguagePlugin> myPlugins;
protected IntellijLintClient(@NonNull Project project) {
myProject = project;
myConverters = LintLanguageExtension.getConverters(project);
myPlugins = LintLanguageExtension.getPlugins(project);
}
/** Creates a lint client for batch inspections */
@@ -393,8 +393,8 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC
}
@Override
public List<UastConverter> getConverters() {
return myConverters;
public List<UastLanguagePlugin> getLanguagePlugins() {
return myPlugins;
}
// Overridden such that lint doesn't complain about missing a bin dir property in the event
@@ -17,8 +17,9 @@
package org.jetbrains.android.inspections.klint
import com.android.tools.klint.client.api.LintLanguageExtension
import org.jetbrains.kotlin.uast.KotlinConverter
import org.jetbrains.kotlin.uast.KotlinUastLanguagePlugin
class KotlinLintLanguageExtension : LintLanguageExtension() {
override fun getConverter() = KotlinConverter
override val converter = KotlinUastLanguagePlugin.converter
override val additionalCheckers = KotlinUastLanguagePlugin.additionalCheckers
}
@@ -14,11 +14,8 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.lint
package org.jetbrains.uast.check
import com.android.tools.klint.client.api.LintLanguageExtension
import org.jetbrains.kotlin.uast.KotlinConverter
import org.jetbrains.uast.UastAdditionalChecker
class KotlinLintLanguageExtension : LintLanguageExtension() {
override fun getConverter() = KotlinConverter
}
interface AndroidUastAdditionalChecker : UastAdditionalChecker
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiManager
import org.jetbrains.uast.*
import org.jetbrains.uast.UastCallKind.Companion.CONSTRUCTOR_CALL
import org.jetbrains.uast.UastCallKind.Companion.FUNCTION_CALL
import org.jetbrains.uast.java.JavaConverter
import org.jetbrains.uast.java.JavaUastLanguagePlugin
import org.jetbrains.uast.visitor.UastVisitor
import java.io.File
@@ -41,12 +41,12 @@ object UastChecker {
fun checkWithCustomHandler(
project: Project,
file: File,
converters: List<UastConverter>,
converters: List<UastLanguagePlugin>,
visitor: UastVisitor) {
check(project, file, converters, UastHandler { visitor.handle(it) })
}
fun check(project: Project, file: File, converters: List<UastConverter>, handler: UastHandler) {
fun check(project: Project, file: File, plugins: List<UastLanguagePlugin>, handler: UastHandler) {
val vfile = VirtualFileManager.getInstance().findFileByUrl("file://" + file.absolutePath) ?: return
ApplicationManager.getApplication().runReadAction {
val psiFile = PsiManager.getInstance(project).findFile(vfile)
@@ -54,11 +54,11 @@ object UastChecker {
if (psiFile != null) {
when (psiFile) {
is PsiJavaFile -> {
val ufile = JavaConverter.convert(psiFile)
ufile.handleTraverse(handler)
val ufile = JavaUastLanguagePlugin.converter.convertWithParent(psiFile)
ufile?.handleTraverse(handler)
}
else -> for (converter in converters) {
val ufile = converter.convertWithParent(psiFile)
else -> for (plugin in plugins) {
val ufile = plugin.converter.convertWithParent(psiFile)
if (ufile != null) {
ufile.handleTraverse(handler)
break
@@ -69,12 +69,20 @@ object UastChecker {
}
}
fun check(project: Project, file: File, scanner: UastScanner, converters: List<UastConverter>, context: UastAndroidContext) {
fun check(project: Project, file: File, scanner: UastScanner, plugins: List<UastLanguagePlugin>, context: UastAndroidContext) {
val applicableFunctionNames = scanner.applicableFunctionNames ?: emptyList()
val applicableSuperClasses = scanner.applicableSuperClasses ?: emptyList()
val applicableConstructorTypes = scanner.applicableConstructorTypes ?: emptyList()
check(project, file, converters, UastHandler { element ->
val additionalCheckers = plugins.fold(mutableListOf<UastAdditionalChecker>()) { list, plugin ->
for (checker in plugin.additionalCheckers) {
if (checker is AndroidUastAdditionalChecker) list += checker
}
list
}
var handler: UastHandler? = null
handler = UastHandler { element ->
when (element) {
is UCallExpression -> {
if (applicableFunctionNames.isNotEmpty()) {
@@ -99,7 +107,13 @@ object UastChecker {
}
}
}
})
for (checker in additionalCheckers) {
checker(element, handler!!, context)
}
}
check(project, file, plugins, handler)
}
}
@@ -16,15 +16,15 @@
package org.jetbrains.uast
interface UastContext {
val converters: List<UastConverter>
val languagePlugins: List<UastLanguagePlugin>
fun convert(element: Any?): UElement? {
if (element == null) {
return null
}
for (converter in converters) {
val uelement = converter.convertWithParent(element)
for (plugin in languagePlugins) {
val uelement = plugin.converter.convertWithParent(element)
if (uelement != null) {
return uelement
}
@@ -22,9 +22,18 @@ interface UastConverter {
fun isFileSupported(path: String): Boolean
}
interface UastLanguagePlugin {
val converter: UastConverter
val additionalCheckers: List<UastAdditionalChecker>
}
interface UastAdditionalChecker {
operator fun invoke(element: UElement, handler: UastHandler, context: UastContext)
}
object UastConverterUtils {
@JvmStatic
fun isFileSupported(converters: List<UastConverter>, path: String): Boolean {
return converters.any { it.isFileSupported(path) }
fun isFileSupported(converters: List<UastLanguagePlugin>, path: String): Boolean {
return converters.any { it.converter.isFileSupported(path) }
}
}
@@ -19,7 +19,12 @@ package org.jetbrains.uast.java
import com.intellij.psi.*
import org.jetbrains.uast.*
object JavaConverter : UastConverter {
object JavaUastLanguagePlugin : UastLanguagePlugin {
override val converter: UastConverter = JavaConverter
override val additionalCheckers = emptyList<UastAdditionalChecker>()
}
internal object JavaConverter : UastConverter {
override fun isFileSupported(path: String): Boolean {
return path.endsWith(".java", ignoreCase = true)
}
@@ -25,9 +25,15 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.uast.lint.PropertyAsCallAndroidUastAdditionalChecker
import org.jetbrains.uast.*
object KotlinConverter : UastConverter {
object KotlinUastLanguagePlugin : UastLanguagePlugin {
override val converter: UastConverter = KotlinConverter
override val additionalCheckers = listOf(PropertyAsCallAndroidUastAdditionalChecker())
}
internal object KotlinConverter : UastConverter {
override fun isFileSupported(path: String) = path.endsWith(".kt", false) || path.endsWith(".kts", false)
override fun convert(element: Any?, parent: UElement): UElement? {
@@ -142,7 +148,7 @@ object KotlinConverter : UastConverter {
is KtBlockExpression -> KotlinUBlockExpression(expression, parent)
is KtConstantExpression -> KotlinULiteralExpression(expression, parent)
is KtTryExpression -> KotlinUTryExpression(expression, parent)
is KtArrayAccessExpression -> KotlinUArrayAccess(expression, parent)
is KtArrayAccessExpression -> KotlinUArrayAccessExpression(expression, parent)
is KtLambdaExpression -> KotlinULambdaExpression(expression, parent)
is KtBinaryExpressionWithTypeRHS -> KotlinUBinaryExpressionWithType(expression, parent)
@@ -166,7 +172,7 @@ object KotlinConverter : UastConverter {
internal fun asSimpleReference(element: PsiElement?, parent: UElement): USimpleReferenceExpression? {
if (element == null) return null
return KotlinUSimpleReferenceExpression(element, KtPsiUtil.unquoteIdentifier(element.text), parent)
return KotlinNameUSimpleReferenceExpression(element, KtPsiUtil.unquoteIdentifier(element.text), parent)
}
internal fun convertOrEmpty(expression: KtExpression?, parent: UElement): UExpression {