From 05db4b3ccff1875266ca39c4165e6c0ff9c09e54 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 28 Mar 2018 17:09:16 +0200 Subject: [PATCH] Optimization: LeakingThisInspection --- .../idea/inspections/LeakingThisInspection.kt | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt index 1b39946a2cf..5070956ec72 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/LeakingThisInspection.kt @@ -14,67 +14,62 @@ import com.intellij.psi.PsiElementVisitor import com.intellij.psi.search.searches.DefinitionsScopedSearch import org.jetbrains.kotlin.cfg.LeakingThisDescriptor.* 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.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtThisExpression -import org.jetbrains.kotlin.psi.expressionVisitor +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.resolve.BindingContext.LEAKING_THIS import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils class LeakingThisInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { - return expressionVisitor { expression -> + return classVisitor { klass -> // We still use analyzeWithAllCompilerChecks() here. // It's possible to use analyze(), but then we should repeat class constructor consistency check // for different class internal elements, like KtProperty and KtClassInitializer. // It can affect performance, so yet we want to avoid this. - @Suppress("DEPRECATION") - val context = expression.analyzeWithAllCompilerChecks().bindingContext - val leakingThisDescriptor = context.get(LEAKING_THIS, expression) ?: return@expressionVisitor - val description = when (leakingThisDescriptor) { - is NonFinalClass -> - if (expression is KtThisExpression) - "Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}" - else - return@expressionVisitor // Not supported yet - is NonFinalProperty -> - "Accessing non-final property ${leakingThisDescriptor.property.name} in constructor" - is NonFinalFunction -> - "Calling non-final function ${leakingThisDescriptor.function.name} in constructor" - else -> return@expressionVisitor // Not supported yet - } - val memberDescriptorToFix = when (leakingThisDescriptor) { - is NonFinalProperty -> leakingThisDescriptor.property - is NonFinalFunction -> leakingThisDescriptor.function - else -> null - } - val memberFix = memberDescriptorToFix?.let { - if (it.modality == Modality.OPEN) { - val modifierListOwner = DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration - createMakeFinalFix(modifierListOwner) + val context = klass.analyzeWithContent() + val leakingThese = context.getSliceContents(LEAKING_THIS) + these@ for ((expression, leakingThisDescriptor) in leakingThese) { + val description = when (leakingThisDescriptor) { + is NonFinalClass -> + if (expression is KtThisExpression) + "Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}" + else + continue@these // Not supported yet + is NonFinalProperty -> + "Accessing non-final property ${leakingThisDescriptor.property.name} in constructor" + is NonFinalFunction -> + "Calling non-final function ${leakingThisDescriptor.function.name} in constructor" + else -> continue@these // Not supported yet + } + val memberDescriptorToFix = when (leakingThisDescriptor) { + is NonFinalProperty -> leakingThisDescriptor.property + is NonFinalFunction -> leakingThisDescriptor.function + else -> null + } + val memberFix = memberDescriptorToFix?.let { + if (it.modality == Modality.OPEN) { + val modifierListOwner = DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtDeclaration + createMakeFinalFix(modifierListOwner) + } else null } - else null - } - val klass = leakingThisDescriptor.classOrObject as? KtClass - val classFix = - if (klass != null && klass.hasModifier(KtTokens.OPEN_KEYWORD)) { + val classFix = + if (klass.hasModifier(KtTokens.OPEN_KEYWORD)) { createMakeFinalFix(klass) - } - else null + } else null - holder.registerProblem( + holder.registerProblem( expression, description, when (leakingThisDescriptor) { is NonFinalProperty, is NonFinalFunction -> GENERIC_ERROR_OR_WARNING else -> WEAK_WARNING }, *(arrayOf(memberFix, classFix).filterNotNull().toTypedArray()) - ) + ) + } } } @@ -84,7 +79,7 @@ class LeakingThisInspection : AbstractKotlinInspection() { declaration ?: return null val useScope = declaration.useScope 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) } }