blob: 17ca1724828e2b39f9a9db89928287f4765324ff [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.lint.client.api
import com.android.SdkConstants.ANDROID_PKG
import com.android.tools.lint.detector.api.Context
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Location
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.XmlScannerConstants
import com.android.tools.lint.detector.api.interprocedural.CallGraphResult
import com.android.tools.lint.detector.api.interprocedural.CallGraphVisitor
import com.android.tools.lint.detector.api.interprocedural.ClassHierarchyVisitor
import com.android.tools.lint.detector.api.interprocedural.IntraproceduralDispatchReceiverVisitor
import com.google.common.base.Joiner
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.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.util.Computable
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiLambdaExpression
import com.intellij.psi.PsiTypeParameter
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UArrayAccessExpression
import org.jetbrains.uast.UBinaryExpression
import org.jetbrains.uast.UBinaryExpressionWithType
import org.jetbrains.uast.UBlockExpression
import org.jetbrains.uast.UBreakExpression
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UCallableReferenceExpression
import org.jetbrains.uast.UCatchClause
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UClassInitializer
import org.jetbrains.uast.UClassLiteralExpression
import org.jetbrains.uast.UContinueExpression
import org.jetbrains.uast.UDeclarationsExpression
import org.jetbrains.uast.UDoWhileExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UEnumConstant
import org.jetbrains.uast.UExpressionList
import org.jetbrains.uast.UField
import org.jetbrains.uast.UFile
import org.jetbrains.uast.UForEachExpression
import org.jetbrains.uast.UForExpression
import org.jetbrains.uast.UIfExpression
import org.jetbrains.uast.UImportStatement
import org.jetbrains.uast.ULabeledExpression
import org.jetbrains.uast.ULambdaExpression
import org.jetbrains.uast.ULiteralExpression
import org.jetbrains.uast.ULocalVariable
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UObjectLiteralExpression
import org.jetbrains.uast.UParameter
import org.jetbrains.uast.UParenthesizedExpression
import org.jetbrains.uast.UPolyadicExpression
import org.jetbrains.uast.UPostfixExpression
import org.jetbrains.uast.UPrefixExpression
import org.jetbrains.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.UReturnExpression
import org.jetbrains.uast.USimpleNameReferenceExpression
import org.jetbrains.uast.USuperExpression
import org.jetbrains.uast.USwitchClauseExpression
import org.jetbrains.uast.USwitchExpression
import org.jetbrains.uast.UThisExpression
import org.jetbrains.uast.UThrowExpression
import org.jetbrains.uast.UTryExpression
import org.jetbrains.uast.UTypeReferenceExpression
import org.jetbrains.uast.UUnaryExpression
import org.jetbrains.uast.UVariable
import org.jetbrains.uast.UWhileExpression
import org.jetbrains.uast.util.isConstructorCall
import org.jetbrains.uast.util.isMethodCall
import org.jetbrains.uast.visitor.AbstractUastVisitor
import java.util.ArrayList
import java.util.HashMap
/**
* Specialized visitor for running detectors on a Java AST.
* It operates in three phases:
*
* 1. First, it computes a set of maps where it generates a map from each
* significant AST attribute (such as method call names) to a list
* of detectors to consult whenever that attribute is encountered.
* Examples of "attributes" are method names, Android resource identifiers,
* and general AST node types such as "cast" nodes etc. These are
* defined on the [SourceCodeScanner] interface.
* 1. Second, it iterates over the document a single time, delegating to
* the detectors found at each relevant AST attribute.
* 1. Finally, it calls the remaining visitors (those that need to process a
* whole document on their own).
*
* It also notifies all the detectors before and after the document is processed
* such that they can do pre- and post-processing.
*/
internal class UElementVisitor constructor(
private val parser: UastParser,
detectors: List<Detector>
) {
private val methodDetectors =
Maps.newHashMapWithExpectedSize<String, MutableList<VisitingDetector>>(90)
private val constructorDetectors =
Maps.newHashMapWithExpectedSize<String, MutableList<VisitingDetector>>(12)
private val referenceDetectors =
Maps.newHashMapWithExpectedSize<String, MutableList<VisitingDetector>>(10)
private val resourceFieldDetectors = ArrayList<VisitingDetector>()
private val allDetectors: MutableList<VisitingDetector>
private val nodePsiTypeDetectors =
Maps.newHashMapWithExpectedSize<Class<out UElement>, MutableList<VisitingDetector>>(25)
private val superClassDetectors = HashMap<String, MutableList<VisitingDetector>>()
private val annotationHandler: AnnotationHandler?
private val callGraphDetectors = ArrayList<SourceCodeScanner>()
init {
allDetectors = ArrayList(detectors.size)
var annotationScanners: Multimap<String, SourceCodeScanner>? = null
for (detector in detectors) {
val uastScanner = detector as SourceCodeScanner
val v = VisitingDetector(detector, uastScanner)
allDetectors.add(v)
val names = detector.getApplicableMethodNames()
if (names != null) {
// not supported in Java visitors; adding a method invocation node is trivial
// for that case.
assert(names !== XmlScannerConstants.ALL)
for (name in names) {
val list = methodDetectors.computeIfAbsent(name) { ArrayList(SAME_TYPE_COUNT) }
list.add(v)
}
}
val applicableSuperClasses = detector.applicableSuperClasses()
if (applicableSuperClasses != null) {
for (fqn in applicableSuperClasses) {
val list =
superClassDetectors.computeIfAbsent(fqn) { ArrayList(SAME_TYPE_COUNT) }
list.add(v)
}
}
val nodePsiTypes = detector.getApplicableUastTypes()
if (nodePsiTypes != null) {
for (type in nodePsiTypes) {
val list =
nodePsiTypeDetectors.computeIfAbsent(type) { ArrayList(SAME_TYPE_COUNT) }
list.add(v)
}
}
val types = detector.getApplicableConstructorTypes()
if (types != null) {
// not supported in Java visitors; adding a method invocation node is trivial
// for that case.
assert(types !== XmlScannerConstants.ALL)
for (type in types) {
var list: MutableList<VisitingDetector>? = constructorDetectors[type]
if (list == null) {
list = ArrayList(SAME_TYPE_COUNT)
constructorDetectors[type] = list
}
list.add(v)
}
}
val referenceNames = detector.getApplicableReferenceNames()
if (referenceNames != null) {
// not supported in Java visitors; adding a method invocation node is trivial
// for that case.
assert(referenceNames !== XmlScannerConstants.ALL)
for (name in referenceNames) {
val list =
referenceDetectors.computeIfAbsent(name) { ArrayList(SAME_TYPE_COUNT) }
list.add(v)
}
}
if (detector.appliesToResourceRefs()) {
resourceFieldDetectors.add(v)
}
val annotations = detector.applicableAnnotations()
if (annotations != null) {
if (annotationScanners == null) {
annotationScanners = ArrayListMultimap.create()
}
for (annotation in annotations) {
annotationScanners!!.put(annotation, uastScanner)
}
}
if (uastScanner.isCallGraphRequired()) {
callGraphDetectors.add(uastScanner)
}
}
val relevantAnnotations: Set<String>?
if (annotationScanners != null) {
annotationHandler = AnnotationHandler(annotationScanners)
relevantAnnotations = annotationHandler.relevantAnnotations
} else {
annotationHandler = null
relevantAnnotations = null
}
parser.evaluator.setRelevantAnnotations(relevantAnnotations)
}
fun visitFile(context: JavaContext) {
try {
val uastParser = context.uastParser
val uFile = uastParser.parse(context) ?: run {
context.client.log(Severity.WARNING, null,
"Lint could not build AST for ${context.file}; ignoring file")
return
}
// (Immediate return if null: No need to log this; the parser should be reporting
// a full warning (such as IssueRegistry#PARSER_ERROR) with details, location, etc.)
val client = context.client
try {
context.setJavaFile(uFile.psi) // needed for getLocation
context.uastFile = uFile
client.runReadAction(Runnable {
for (v in allDetectors) {
v.setContext(context)
v.detector.beforeCheckFile(context)
}
})
if (!superClassDetectors.isEmpty()) {
client.runReadAction(Runnable {
val visitor = SuperclassPsiVisitor(context)
uFile.accept(visitor)
})
}
if (!methodDetectors.isEmpty() ||
!resourceFieldDetectors.isEmpty() ||
!constructorDetectors.isEmpty() ||
!referenceDetectors.isEmpty() ||
annotationHandler != null
) {
client.runReadAction(Runnable {
// TODO: Do we need to break this one up into finer grain locking units
val visitor = DelegatingPsiVisitor(context)
uFile.accept(visitor)
})
} else {
// Note that the DelegatingPsiVisitor is a subclass of DispatchPsiVisitor
// so the above includes the below as well (through super classes)
if (!nodePsiTypeDetectors.isEmpty()) {
client.runReadAction(Runnable {
// TODO: Do we need to break this one up into finer grain locking units
val visitor = DispatchPsiVisitor()
uFile.accept(visitor)
})
}
}
client.runReadAction(Runnable {
for (v in allDetectors) {
ProgressManager.checkCanceled()
v.detector.afterCheckFile(context)
}
})
} finally {
parser.dispose(context, uFile)
context.setJavaFile(null)
context.uastFile = null
}
} catch (e: ProcessCanceledException) {
// Cancelling inspections in the IDE
throw e
} catch (e: Throwable) {
// Don't allow lint bugs to take down the whole build. TRY to log this as a
// lint error instead!
LintDriver.handleDetectorError(context, context.driver, e)
}
}
fun prepare(contexts: List<JavaContext>, testContexts: List<JavaContext>): Boolean =
parser.prepare(contexts, testContexts)
fun visitGroups(
projectContext: Context,
allContexts: List<JavaContext>
) {
if (!allContexts.isEmpty() && allDetectors.stream()
.anyMatch { it.uastScanner.isCallGraphRequired() }
) {
val callGraph = projectContext.client.runReadAction(Computable {
generateCallGraph(projectContext, parser, allContexts)
})
if (callGraph != null && !callGraphDetectors.isEmpty()) {
for (scanner in callGraphDetectors) {
projectContext.client.runReadAction(Runnable {
ProgressManager.checkCanceled()
scanner.analyzeCallGraph(projectContext, callGraph)
})
}
}
}
}
private fun generateCallGraph(
projectContext: Context,
parser: UastParser,
contexts: List<JavaContext>
): CallGraphResult? {
if (contexts.isEmpty()) {
return null
}
try {
val chaVisitor = ClassHierarchyVisitor()
val receiverEvalVisitor =
IntraproceduralDispatchReceiverVisitor(chaVisitor.classHierarchy)
val callGraphVisitor = CallGraphVisitor(
receiverEvalVisitor.receiverEval,
chaVisitor.classHierarchy, false
)
for (context in contexts) {
val uFile = parser.parse(context)
uFile?.accept(chaVisitor)
}
for (context in contexts) {
val uFile = parser.parse(context)
uFile?.accept(receiverEvalVisitor)
}
for (context in contexts) {
val uFile = parser.parse(context)
uFile?.accept(callGraphVisitor)
}
val callGraph = callGraphVisitor.callGraph
val receiverEval = receiverEvalVisitor.receiverEval
return CallGraphResult(callGraph, receiverEval)
} catch (oom: OutOfMemoryError) {
val detectors = Lists.newArrayList<String>()
for (detector in callGraphDetectors) {
detectors.add(detector.javaClass.simpleName)
}
val detectorNames = "[" + Joiner.on(", ").join(detectors) + "]"
var message = "Lint ran out of memory while building a callgraph (requested by " +
"these detectors: " + detectorNames + "). You can either disable these " +
"checks, or give lint more heap space."
if (LintClient.isGradle) {
message += " For example, to set the Gradle daemon to use 4 GB, edit " +
"`gradle.properties` to contains `org.gradle.jvmargs=-Xmx4g`"
}
projectContext.report(
IssueRegistry.LINT_ERROR,
Location.create(projectContext.project.dir), message
)
return null
}
}
fun dispose() {
parser.dispose()
}
private class VisitingDetector(val detector: Detector, val uastScanner: SourceCodeScanner) {
private var mVisitor: UElementHandler? = null
private var mContext: JavaContext? = null
val visitor: UElementHandler
get() {
if (mVisitor == null) {
mVisitor = detector.createUastHandler(mContext!!)
if (mVisitor == null) {
mVisitor = UElementHandler.NONE
}
}
return mVisitor!!
}
fun setContext(context: JavaContext) {
mContext = context
// The visitors are one-per-context, so clear them out here and construct
// lazily only if needed
mVisitor = null
}
}
private inner class SuperclassPsiVisitor(private val context: JavaContext) :
AbstractUastVisitor() {
override fun visitLambdaExpression(node: ULambdaExpression): Boolean {
// Have to go to PSI here; not available on ULambdaExpression yet
// https://github.com/JetBrains/uast/issues/16
// ULambdaExpression#getFunctionalInterfaceType
val psi = node.psi
if (psi is PsiLambdaExpression) {
val type = psi.functionalInterfaceType
if (type is PsiClassType) {
val resolved = type.resolve()
if (resolved != null) {
checkClass(node, null, resolved)
}
}
}
return super.visitLambdaExpression(node)
}
override fun visitClass(node: UClass): Boolean {
val result = super.visitClass(node)
checkClass(null, node, node)
return result
}
private fun checkClass(
lambda: ULambdaExpression?,
uClass: UClass?,
node: PsiClass
) {
ProgressManager.checkCanceled()
if (node is PsiTypeParameter) {
// Not included: explained in javadoc for JavaPsiScanner#checkClass
return
}
var cls: PsiClass? = node
var depth = 0
while (cls != null) {
var list: List<VisitingDetector>? = superClassDetectors[cls.qualifiedName]
if (list != null) {
for (v in list) {
val uastScanner = v.uastScanner
if (uClass != null) {
uastScanner.visitClass(context, uClass)
} else {
assert(lambda != null)
uastScanner.visitClass(context, lambda!!)
}
}
}
// Check interfaces too
val interfaceNames = getInterfaceNames(null, cls)
if (interfaceNames != null) {
for (name in interfaceNames) {
list = superClassDetectors[name]
if (list != null) {
for (v in list) {
val uastScanner = v.uastScanner
if (uClass != null) {
uastScanner.visitClass(context, uClass)
} else {
assert(lambda != null)
uastScanner.visitClass(context, lambda!!)
}
}
}
}
}
cls = cls.superClass
depth++
if (depth == 500) {
// Shouldn't happen in practice; this prevents the IDE from
// hanging if the user has accidentally typed in an incorrect
// super class which creates a cycle.
break
}
}
}
}
private open inner class DispatchPsiVisitor : AbstractUastVisitor() {
override fun visitAnnotation(node: UAnnotation): Boolean {
val list = nodePsiTypeDetectors[UAnnotation::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitAnnotation(node)
}
}
return super.visitAnnotation(node)
}
override fun visitArrayAccessExpression(node: UArrayAccessExpression): Boolean {
val list = nodePsiTypeDetectors[UArrayAccessExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitArrayAccessExpression(node)
}
}
return super.visitArrayAccessExpression(node)
}
override fun visitBinaryExpression(node: UBinaryExpression): Boolean {
val list = nodePsiTypeDetectors[UBinaryExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitBinaryExpression(node)
}
}
return super.visitBinaryExpression(node)
}
override fun visitBinaryExpressionWithType(node: UBinaryExpressionWithType): Boolean {
val list = nodePsiTypeDetectors[UBinaryExpressionWithType::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitBinaryExpressionWithType(node)
}
}
return super.visitBinaryExpressionWithType(node)
}
override fun visitBlockExpression(node: UBlockExpression): Boolean {
val list = nodePsiTypeDetectors[UBlockExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitBlockExpression(node)
}
}
return super.visitBlockExpression(node)
}
override fun visitBreakExpression(node: UBreakExpression): Boolean {
val list = nodePsiTypeDetectors[UBreakExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitBreakExpression(node)
}
}
return super.visitBreakExpression(node)
}
override fun visitCallExpression(node: UCallExpression): Boolean {
val list = nodePsiTypeDetectors[UCallExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitCallExpression(node)
}
}
return super.visitCallExpression(node)
}
override fun visitCallableReferenceExpression(node: UCallableReferenceExpression): Boolean {
val list = nodePsiTypeDetectors[UCallableReferenceExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitCallableReferenceExpression(node)
}
}
return super.visitCallableReferenceExpression(node)
}
override fun visitCatchClause(node: UCatchClause): Boolean {
val list = nodePsiTypeDetectors[UCatchClause::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitCatchClause(node)
}
}
return super.visitCatchClause(node)
}
override fun visitClass(node: UClass): Boolean {
val list = nodePsiTypeDetectors[UClass::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitClass(node)
}
}
return super.visitClass(node)
}
override fun visitClassLiteralExpression(node: UClassLiteralExpression): Boolean {
val list = nodePsiTypeDetectors[UClassLiteralExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitClassLiteralExpression(node)
}
}
return super.visitClassLiteralExpression(node)
}
override fun visitContinueExpression(node: UContinueExpression): Boolean {
val list = nodePsiTypeDetectors[UContinueExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitContinueExpression(node)
}
}
return super.visitContinueExpression(node)
}
override fun visitDeclarationsExpression(node: UDeclarationsExpression): Boolean {
val list = nodePsiTypeDetectors[UDeclarationsExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitDeclarationsExpression(node)
}
}
return super.visitDeclarationsExpression(node)
}
override fun visitDoWhileExpression(node: UDoWhileExpression): Boolean {
val list = nodePsiTypeDetectors[UDoWhileExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitDoWhileExpression(node)
}
}
return super.visitDoWhileExpression(node)
}
override fun visitElement(node: UElement): Boolean {
val list = nodePsiTypeDetectors[UElement::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitElement(node)
}
}
return super.visitElement(node)
}
override fun visitEnumConstant(node: UEnumConstant): Boolean {
val list = nodePsiTypeDetectors[UEnumConstant::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitEnumConstant(node)
}
}
return super.visitEnumConstant(node)
}
override fun visitExpressionList(node: UExpressionList): Boolean {
val list = nodePsiTypeDetectors[UExpressionList::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitExpressionList(node)
}
}
return super.visitExpressionList(node)
}
override fun visitField(node: UField): Boolean {
val list = nodePsiTypeDetectors[UField::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitField(node)
}
}
return super.visitField(node)
}
override fun visitFile(node: UFile): Boolean {
val list = nodePsiTypeDetectors[UFile::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitFile(node)
}
}
return super.visitFile(node)
}
override fun visitForEachExpression(node: UForEachExpression): Boolean {
val list = nodePsiTypeDetectors[UForEachExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitForEachExpression(node)
}
}
return super.visitForEachExpression(node)
}
override fun visitForExpression(node: UForExpression): Boolean {
val list = nodePsiTypeDetectors[UForExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitForExpression(node)
}
}
return super.visitForExpression(node)
}
override fun visitIfExpression(node: UIfExpression): Boolean {
val list = nodePsiTypeDetectors[UIfExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitIfExpression(node)
}
}
return super.visitIfExpression(node)
}
override fun visitImportStatement(node: UImportStatement): Boolean {
val list = nodePsiTypeDetectors[UImportStatement::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitImportStatement(node)
}
}
return super.visitImportStatement(node)
}
override fun visitInitializer(node: UClassInitializer): Boolean {
val list = nodePsiTypeDetectors[UClassInitializer::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitInitializer(node)
}
}
return super.visitInitializer(node)
}
override fun visitLabeledExpression(node: ULabeledExpression): Boolean {
val list = nodePsiTypeDetectors[ULabeledExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitLabeledExpression(node)
}
}
return super.visitLabeledExpression(node)
}
override fun visitLambdaExpression(node: ULambdaExpression): Boolean {
val list = nodePsiTypeDetectors[ULambdaExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitLambdaExpression(node)
}
}
return super.visitLambdaExpression(node)
}
override fun visitLiteralExpression(node: ULiteralExpression): Boolean {
val list = nodePsiTypeDetectors[ULiteralExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitLiteralExpression(node)
}
}
return super.visitLiteralExpression(node)
}
override fun visitLocalVariable(node: ULocalVariable): Boolean {
val list = nodePsiTypeDetectors[ULocalVariable::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitLocalVariable(node)
}
}
return super.visitLocalVariable(node)
}
override fun visitMethod(node: UMethod): Boolean {
val list = nodePsiTypeDetectors[UMethod::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitMethod(node)
}
}
return super.visitMethod(node)
}
override fun visitObjectLiteralExpression(node: UObjectLiteralExpression): Boolean {
val list = nodePsiTypeDetectors[UObjectLiteralExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitObjectLiteralExpression(node)
}
}
return super.visitObjectLiteralExpression(node)
}
override fun visitParameter(node: UParameter): Boolean {
val list = nodePsiTypeDetectors[UParameter::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitParameter(node)
}
}
return super.visitParameter(node)
}
override fun visitParenthesizedExpression(node: UParenthesizedExpression): Boolean {
val list = nodePsiTypeDetectors[UParenthesizedExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitParenthesizedExpression(node)
}
}
return super.visitParenthesizedExpression(node)
}
override fun visitPolyadicExpression(node: UPolyadicExpression): Boolean {
val list = nodePsiTypeDetectors[UPolyadicExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitPolyadicExpression(node)
}
}
return super.visitPolyadicExpression(node)
}
override fun visitPostfixExpression(node: UPostfixExpression): Boolean {
val list = nodePsiTypeDetectors[UPostfixExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitPostfixExpression(node)
}
}
return super.visitPostfixExpression(node)
}
override fun visitPrefixExpression(node: UPrefixExpression): Boolean {
val list = nodePsiTypeDetectors[UPrefixExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitPrefixExpression(node)
}
}
return super.visitPrefixExpression(node)
}
override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression): Boolean {
val list = nodePsiTypeDetectors[UQualifiedReferenceExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitQualifiedReferenceExpression(node)
}
}
return super.visitQualifiedReferenceExpression(node)
}
override fun visitReturnExpression(node: UReturnExpression): Boolean {
val list = nodePsiTypeDetectors[UReturnExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitReturnExpression(node)
}
}
return super.visitReturnExpression(node)
}
override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
val list = nodePsiTypeDetectors[USimpleNameReferenceExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitSimpleNameReferenceExpression(node)
}
}
return super.visitSimpleNameReferenceExpression(node)
}
override fun visitSuperExpression(node: USuperExpression): Boolean {
val list = nodePsiTypeDetectors[USuperExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitSuperExpression(node)
}
}
return super.visitSuperExpression(node)
}
override fun visitSwitchClauseExpression(node: USwitchClauseExpression): Boolean {
val list = nodePsiTypeDetectors[USwitchClauseExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitSwitchClauseExpression(node)
}
}
return super.visitSwitchClauseExpression(node)
}
override fun visitSwitchExpression(node: USwitchExpression): Boolean {
val list = nodePsiTypeDetectors[USwitchExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitSwitchExpression(node)
}
}
return super.visitSwitchExpression(node)
}
override fun visitThisExpression(node: UThisExpression): Boolean {
val list = nodePsiTypeDetectors[UThisExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitThisExpression(node)
}
}
return super.visitThisExpression(node)
}
override fun visitThrowExpression(node: UThrowExpression): Boolean {
val list = nodePsiTypeDetectors[UThrowExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitThrowExpression(node)
}
}
return super.visitThrowExpression(node)
}
override fun visitTryExpression(node: UTryExpression): Boolean {
val list = nodePsiTypeDetectors[UTryExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitTryExpression(node)
}
}
return super.visitTryExpression(node)
}
override fun visitTypeReferenceExpression(node: UTypeReferenceExpression): Boolean {
val list = nodePsiTypeDetectors[UTypeReferenceExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitTypeReferenceExpression(node)
}
}
return super.visitTypeReferenceExpression(node)
}
override fun visitUnaryExpression(node: UUnaryExpression): Boolean {
val list = nodePsiTypeDetectors[UUnaryExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitUnaryExpression(node)
}
}
return super.visitUnaryExpression(node)
}
override fun visitVariable(node: UVariable): Boolean {
val list = nodePsiTypeDetectors[UVariable::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitVariable(node)
}
}
return super.visitVariable(node)
}
override fun visitWhileExpression(node: UWhileExpression): Boolean {
val list = nodePsiTypeDetectors[UWhileExpression::class.java]
if (list != null) {
for (v in list) {
v.visitor.visitWhileExpression(node)
}
}
return super.visitWhileExpression(node)
}
}
/** Performs common AST searches for method calls and R-type-field references.
* Note that this is a specialized form of the [DispatchPsiVisitor]. */
private inner class DelegatingPsiVisitor constructor(private val mContext: JavaContext) :
DispatchPsiVisitor() {
private val mVisitResources: Boolean = !resourceFieldDetectors.isEmpty()
private val mVisitMethods: Boolean = !methodDetectors.isEmpty()
private val mVisitConstructors: Boolean = !constructorDetectors.isEmpty()
private val mVisitReferences: Boolean = !referenceDetectors.isEmpty()
override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
if (mVisitReferences || mVisitResources) {
ProgressManager.checkCanceled()
}
if (mVisitReferences) {
val list = referenceDetectors[node.identifier]
if (list != null) {
val referenced = node.resolve()
if (referenced != null) {
for (v in list) {
val uastScanner = v.uastScanner
uastScanner.visitReference(mContext, node, referenced)
}
}
}
}
if (mVisitResources) {
val reference = ResourceReference.get(node)
if (reference != null) {
for (v in resourceFieldDetectors) {
val uastScanner = v.uastScanner
uastScanner.visitResourceReference(
mContext,
reference.node,
reference.type,
reference.name,
reference.`package` == ANDROID_PKG
)
}
}
}
annotationHandler?.visitSimpleNameReferenceExpression(mContext, node)
return super.visitSimpleNameReferenceExpression(node)
}
override fun visitCallExpression(node: UCallExpression): Boolean {
val result = super.visitCallExpression(node)
ProgressManager.checkCanceled()
if (node.isMethodCall()) {
visitMethodCallExpression(node)
} else if (node.isConstructorCall()) {
visitNewExpression(node)
}
annotationHandler?.visitCallExpression(mContext, node)
return result
}
private fun visitMethodCallExpression(node: UCallExpression) {
if (mVisitMethods) {
val methodName = node.methodName
if (methodName != null) {
val list = methodDetectors[methodName]
if (list != null) {
val function = node.resolve()
if (function != null) {
for (v in list) {
val scanner = v.uastScanner
scanner.visitMethodCall(mContext, node, function)
}
}
}
}
}
}
private fun visitNewExpression(node: UCallExpression) {
if (mVisitConstructors) {
val method = node.resolve() ?: return
val resolvedClass = method.containingClass
if (resolvedClass != null) {
val list = constructorDetectors[resolvedClass.qualifiedName]
if (list != null) {
for (v in list) {
val javaPsiScanner = v.uastScanner
javaPsiScanner.visitConstructor(mContext, node, method)
}
}
}
}
}
// Annotations
// (visitCallExpression handled above)
override fun visitMethod(node: UMethod): Boolean {
annotationHandler?.visitMethod(mContext, node)
return super.visitMethod(node)
}
override fun visitAnnotation(node: UAnnotation): Boolean {
annotationHandler?.visitAnnotation(mContext, node)
return super.visitAnnotation(node)
}
override fun visitEnumConstant(node: UEnumConstant): Boolean {
annotationHandler?.visitEnumConstant(mContext, node)
return super.visitEnumConstant(node)
}
override fun visitArrayAccessExpression(node: UArrayAccessExpression): Boolean {
annotationHandler?.visitArrayAccessExpression(mContext, node)
return super.visitArrayAccessExpression(node)
}
override fun visitVariable(node: UVariable): Boolean {
annotationHandler?.visitVariable(mContext, node)
return super.visitVariable(node)
}
override fun visitClass(node: UClass): Boolean {
annotationHandler?.visitClass(mContext, node)
return super.visitClass(node)
}
}
companion object {
/** Default size of lists holding detectors of the same type for a given node type */
private const val SAME_TYPE_COUNT = 8
private fun getInterfaceNames(
addTo: MutableSet<String>?,
cls: PsiClass
): Set<String>? {
var target = addTo
for (resolvedInterface in cls.interfaces) {
val name = resolvedInterface.qualifiedName ?: continue
if (target == null) {
target = Sets.newHashSet()
} else if (target.contains(name)) {
// Superclasses can explicitly implement the same interface,
// so keep track of visited interfaces as we traverse up the
// super class chain to avoid checking the same interface
// more than once.
continue
}
target!!.add(name)
getInterfaceNames(target, resolvedInterface)
}
return target
}
}
}