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 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)
}
}