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.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
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.psi.psiUtil.getStrictParentOfType
|
||||||
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
|
||||||
@@ -36,10 +37,17 @@ class LeakingThisInspection : AbstractKotlinInspection() {
|
|||||||
if (leakingThisDescriptor.classOrObject != klass) continue@these
|
if (leakingThisDescriptor.classOrObject != klass) continue@these
|
||||||
val description = when (leakingThisDescriptor) {
|
val description = when (leakingThisDescriptor) {
|
||||||
is NonFinalClass ->
|
is NonFinalClass ->
|
||||||
if (expression is KtThisExpression && expression.getStrictParentOfType<KtClassLiteralExpression>() == null)
|
if (expression is KtThisExpression && expression.getStrictParentOfType<KtClassLiteralExpression>() == null) {
|
||||||
"Leaking 'this' in constructor of non-final class ${leakingThisDescriptor.klass.name}"
|
if (klass.isEnum()) {
|
||||||
else
|
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
|
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 ->
|
||||||
@@ -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 {
|
companion object {
|
||||||
private fun createMakeFinalFix(declaration: KtDeclaration?): IntentionWrapper? {
|
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);
|
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")
|
@TestMetadata("inClassLiteral.kt")
|
||||||
public void testInClassLiteral() throws Exception {
|
public void testInClassLiteral() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/leakingThis/inClassLiteral.kt");
|
runTest("idea/testData/inspectionsLocal/leakingThis/inClassLiteral.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user