Optimization: LeakingThisInspection

This commit is contained in:
Mikhail Glukhikh
2018-03-28 17:09:16 +02:00
parent 5e9ca6f466
commit 05db4b3ccf
@@ -14,67 +14,62 @@ import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.search.searches.DefinitionsScopedSearch import com.intellij.psi.search.searches.DefinitionsScopedSearch
import org.jetbrains.kotlin.cfg.LeakingThisDescriptor.* import org.jetbrains.kotlin.cfg.LeakingThisDescriptor.*
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.psi.expressionVisitor
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext.LEAKING_THIS import org.jetbrains.kotlin.resolve.BindingContext.LEAKING_THIS
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
class LeakingThisInspection : AbstractKotlinInspection() { class LeakingThisInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return expressionVisitor { expression -> return classVisitor { klass ->
// We still use analyzeWithAllCompilerChecks() here. // We still use analyzeWithAllCompilerChecks() here.
// It's possible to use analyze(), but then we should repeat class constructor consistency check // It's possible to use analyze(), but then we should repeat class constructor consistency check
// for different class internal elements, like KtProperty and KtClassInitializer. // for different class internal elements, like KtProperty and KtClassInitializer.
// It can affect performance, so yet we want to avoid this. // It can affect performance, so yet we want to avoid this.
@Suppress("DEPRECATION") val context = klass.analyzeWithContent()
val context = expression.analyzeWithAllCompilerChecks().bindingContext val leakingThese = context.getSliceContents(LEAKING_THIS)
val leakingThisDescriptor = context.get(LEAKING_THIS, expression) ?: return@expressionVisitor these@ for ((expression, leakingThisDescriptor) in leakingThese) {
val description = when (leakingThisDescriptor) { val description = when (leakingThisDescriptor) {
is NonFinalClass -> is NonFinalClass ->
if (expression is KtThisExpression) if (expression is KtThisExpression)
"Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}" "Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}"
else else
return@expressionVisitor // Not supported yet continue@these // Not supported yet
is NonFinalProperty -> is NonFinalProperty ->
"Accessing non-final property ${leakingThisDescriptor.property.name} in constructor" "Accessing non-final property ${leakingThisDescriptor.property.name} in constructor"
is NonFinalFunction -> is NonFinalFunction ->
"Calling non-final function ${leakingThisDescriptor.function.name} in constructor" "Calling non-final function ${leakingThisDescriptor.function.name} in constructor"
else -> return@expressionVisitor // Not supported yet else -> continue@these // Not supported yet
} }
val memberDescriptorToFix = when (leakingThisDescriptor) { val memberDescriptorToFix = when (leakingThisDescriptor) {
is NonFinalProperty -> leakingThisDescriptor.property is NonFinalProperty -> leakingThisDescriptor.property
is NonFinalFunction -> leakingThisDescriptor.function is NonFinalFunction -> leakingThisDescriptor.function
else -> null else -> null
} }
val memberFix = memberDescriptorToFix?.let { val memberFix = memberDescriptorToFix?.let {
if (it.modality == Modality.OPEN) { if (it.modality == Modality.OPEN) {
val modifierListOwner = DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration val modifierListOwner = DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration
createMakeFinalFix(modifierListOwner) createMakeFinalFix(modifierListOwner)
} else null
} }
else null
}
val klass = leakingThisDescriptor.classOrObject as? KtClass val classFix =
val classFix = if (klass.hasModifier(KtTokens.OPEN_KEYWORD)) {
if (klass != null && klass.hasModifier(KtTokens.OPEN_KEYWORD)) {
createMakeFinalFix(klass) createMakeFinalFix(klass)
} } else null
else null
holder.registerProblem( holder.registerProblem(
expression, description, expression, description,
when (leakingThisDescriptor) { when (leakingThisDescriptor) {
is NonFinalProperty, is NonFinalFunction -> GENERIC_ERROR_OR_WARNING is NonFinalProperty, is NonFinalFunction -> GENERIC_ERROR_OR_WARNING
else -> WEAK_WARNING else -> WEAK_WARNING
}, },
*(arrayOf(memberFix, classFix).filterNotNull().toTypedArray()) *(arrayOf(memberFix, classFix).filterNotNull().toTypedArray())
) )
}
} }
} }
@@ -84,7 +79,7 @@ class LeakingThisInspection : AbstractKotlinInspection() {
declaration ?: return null declaration ?: return null
val useScope = declaration.useScope val useScope = declaration.useScope
if (DefinitionsScopedSearch.search(declaration, useScope).findFirst() != null) return null if (DefinitionsScopedSearch.search(declaration, useScope).findFirst() != null) return null
if ((declaration.containingClassOrObject as? KtClass)?.isInterface() ?: false) return null if ((declaration.containingClassOrObject as? KtClass)?.isInterface() == true) return null
return IntentionWrapper(AddModifierFix(declaration, KtTokens.FINAL_KEYWORD), declaration.containingFile) return IntentionWrapper(AddModifierFix(declaration, KtTokens.FINAL_KEYWORD), declaration.containingFile)
} }
} }