Lint: Rewrite Lint diagnostics using Uast.

This commit is contained in:
Yan Zhulanow
2016-04-06 16:05:50 +03:00
parent 126cc778e4
commit 4df9e9261e
210 changed files with 6176 additions and 7039 deletions
@@ -0,0 +1,105 @@
/*
* 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 org.jetbrains.uast.check
import com.android.tools.klint.detector.api.Issue
import com.android.tools.klint.detector.api.JavaContext
import com.android.tools.klint.detector.api.Location
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiJavaFile
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.visitor.UastVisitor
import java.io.File
interface UastAndroidContext : UastContext {
val lintContext: JavaContext
fun report(issue: Issue, element: UElement, location: Location?, message: String)
fun getLocation(element: UElement?) = element?.getLocation()
}
object UastChecker {
fun checkWithCustomHandler(
project: Project,
file: File,
converters: List<UastConverter>,
visitor: UastVisitor) {
check(project, file, converters, UastHandler { visitor.handle(it) })
}
fun check(project: Project, file: File, converters: List<UastConverter>, handler: UastHandler) {
val vfile = VirtualFileManager.getInstance().findFileByUrl("file://" + file.absolutePath) ?: return
ApplicationManager.getApplication().runReadAction {
val psiFile = PsiManager.getInstance(project).findFile(vfile)
if (psiFile != null) {
when (psiFile) {
is PsiJavaFile -> {
val ufile = JavaConverter.convert(psiFile)
ufile.handleTraverse(handler)
}
else -> for (converter in converters) {
val ufile = converter.convertWithParent(psiFile)
if (ufile != null) {
ufile.handleTraverse(handler)
break
}
}
}
}
}
}
fun check(project: Project, file: File, scanner: UastScanner, converters: List<UastConverter>, context: UastAndroidContext) {
val applicableFunctionNames = scanner.applicableFunctionNames ?: emptyList()
val applicableSuperClasses = scanner.applicableSuperClasses ?: emptyList()
val applicableConstructorTypes = scanner.applicableConstructorTypes ?: emptyList()
check(project, file, converters, UastHandler { element ->
when (element) {
is UCallExpression -> {
if (applicableFunctionNames.isNotEmpty()) {
if (element.kind == FUNCTION_CALL && element.functionName in applicableFunctionNames) {
scanner.visitFunctionCall(context, element)
}
}
if (applicableConstructorTypes.isNotEmpty()) {
if (element.kind == CONSTRUCTOR_CALL) {
element.resolve(context)?.let { constructor ->
if (constructor.getContainingClass()?.fqName in applicableConstructorTypes) {
scanner.visitConstructor(context, element, constructor)
}
}
}
}
}
is UClass -> if (applicableSuperClasses.isNotEmpty()) {
if (applicableSuperClasses.any { element.isSubclassOf(it) }) {
scanner.visitClass(context, element)
}
}
}
})
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2015 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 org.jetbrains.uast.check;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UFunction;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.visitor.UastVisitor;
import java.util.List;
public interface UastScanner {
UastVisitor createUastVisitor(UastAndroidContext context);
List<String> getApplicableFunctionNames();
List<String> getApplicableSuperClasses();
List<String> getApplicableConstructorTypes();
void visitFunctionCall(UastAndroidContext context, UCallExpression node);
void visitClass(UastAndroidContext context, UClass node);
void visitConstructor(UastAndroidContext context, UCallExpression functionCall, UFunction constructor);
boolean appliesToResourceRefs();
void visitResourceReference(UastAndroidContext context, UElement element, String type, String name, boolean isFramework);
}
@@ -0,0 +1,32 @@
/*
* 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.
*/
@file:JvmName("UastAndroidUtils")
package org.jetbrains.uast.check
import com.android.tools.klint.detector.api.Location
import com.intellij.openapi.vfs.VfsUtilCore
import org.jetbrains.uast.UElement
import org.jetbrains.uast.psi.PsiElementBacked
fun UElement?.getLocation(): Location? {
val psiElementBacked = this as? PsiElementBacked ?: return null
val psiElement = psiElementBacked.psi
val psiFile = psiElement?.containingFile ?: return null
val vfile = psiFile.virtualFile
val file = VfsUtilCore.virtualToIoFile(vfile)
val range = psiElement?.textRange ?: return null
return Location.create(file, psiFile.text, range.startOffset, range.endOffset)
}
@@ -0,0 +1,16 @@
<?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="kotlin-runtime" level="project" />
<orderEntry type="library" name="intellij-core" level="project" />
<orderEntry type="module" module-name="uast-common" exported="" />
<orderEntry type="module" module-name="uast-java" exported="" />
<orderEntry type="module" module-name="lint-api" />
</component>
</module>