Optimization: LeakingThisInspection
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user