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