Leaking this: fix for 'this' in enum class #KT-15835 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
14311e77c2
commit
2a03e1b3da
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.LEAKING_THIS
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
@@ -36,10 +37,17 @@ class LeakingThisInspection : AbstractKotlinInspection() {
|
||||
if (leakingThisDescriptor.classOrObject != klass) continue@these
|
||||
val description = when (leakingThisDescriptor) {
|
||||
is NonFinalClass ->
|
||||
if (expression is KtThisExpression && expression.getStrictParentOfType<KtClassLiteralExpression>() == null)
|
||||
"Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}"
|
||||
else
|
||||
if (expression is KtThisExpression && expression.getStrictParentOfType<KtClassLiteralExpression>() == null) {
|
||||
if (klass.isEnum()) {
|
||||
val enumEntries = klass.body?.getChildrenOfType<KtEnumEntry>() ?: continue@these
|
||||
if (enumEntries.none { it.hasOverriddenMember() }) continue@these
|
||||
"Leaking 'this' in constructor of enum class ${leakingThisDescriptor.klass.name} (with overridable members)"
|
||||
} else {
|
||||
"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 ->
|
||||
@@ -75,6 +83,9 @@ class LeakingThisInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtEnumEntry.hasOverriddenMember(): Boolean {
|
||||
return body?.getChildrenOfType<KtModifierListOwner>()?.any { it.hasModifier(KtTokens.OVERRIDE_KEYWORD) } == true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun createMakeFinalFix(declaration: KtDeclaration?): IntentionWrapper? {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
enum class Foo {
|
||||
ONE,
|
||||
TWO;
|
||||
val bar = Bar(<caret>this)
|
||||
}
|
||||
|
||||
class Bar(val foo: Foo)
|
||||
@@ -0,0 +1,20 @@
|
||||
// PROBLEM: Leaking 'this' in constructor of enum class Foo (with overridable members)
|
||||
// FIX: none
|
||||
enum class Foo {
|
||||
ONE {
|
||||
override val x = 1
|
||||
},
|
||||
TWO {
|
||||
override val x = 2
|
||||
};
|
||||
|
||||
abstract val x: Int
|
||||
val double = double(<caret>this)
|
||||
}
|
||||
|
||||
fun double(foo: Foo) = foo.x * foo.x
|
||||
|
||||
fun test() {
|
||||
Foo.ONE.double
|
||||
Foo.TWO.double
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// PROBLEM: Leaking 'this' in constructor of enum class Foo (with overridable members)
|
||||
// FIX: none
|
||||
enum class Foo : Bar {
|
||||
ONE {
|
||||
override val x = 1
|
||||
},
|
||||
TWO {
|
||||
override val x = 2
|
||||
};
|
||||
|
||||
val double = double(<caret>this)
|
||||
}
|
||||
|
||||
interface Bar {
|
||||
val x: Int
|
||||
}
|
||||
|
||||
fun double(foo: Foo) = foo.x * foo.x
|
||||
|
||||
fun test() {
|
||||
Foo.ONE.double
|
||||
Foo.TWO.double
|
||||
}
|
||||
+15
@@ -2705,6 +2705,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/leakingThis"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("enum.kt")
|
||||
public void testEnum() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/leakingThis/enum.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryHasOverriddenMember.kt")
|
||||
public void testEnumEntryHasOverriddenMember() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/leakingThis/enumEntryHasOverriddenMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryHasOverriddenMember2.kt")
|
||||
public void testEnumEntryHasOverriddenMember2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/leakingThis/enumEntryHasOverriddenMember2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inClassLiteral.kt")
|
||||
public void testInClassLiteral() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/leakingThis/inClassLiteral.kt");
|
||||
|
||||
Reference in New Issue
Block a user