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