From 7c6d5c825ce08b8755a36297c48bf5df715d747e Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 9 Jan 2017 17:52:20 +0300 Subject: [PATCH] Join declaration & assignment is no more suggested in case of potential smart cast available #KT-15412 Fixed --- .../JoinDeclarationAndAssignmentIntention.kt | 23 ++++++++++++++++--- .../assignmentForFlexible.kt | 6 +++++ .../assignmentForFlexible.kt.after | 5 ++++ .../assignmentForSmartCast.kt | 6 +++++ .../intentions/IntentionTestGenerated.java | 12 ++++++++++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt create mode 100644 idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt.after create mode 100644 idea/testData/intentions/joinDeclarationAndAssignment/assignmentForSmartCast.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/JoinDeclarationAndAssignmentIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/JoinDeclarationAndAssignmentIntention.kt index 702b98c3f6b..1032778c014 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/JoinDeclarationAndAssignmentIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/JoinDeclarationAndAssignmentIntention.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.ScrollType import com.intellij.psi.PsiElement import com.intellij.psi.PsiReferenceService import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.canOmitDeclaredType import org.jetbrains.kotlin.idea.core.moveCaret import org.jetbrains.kotlin.idea.core.unblockDocument @@ -28,6 +29,10 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils class JoinDeclarationAndAssignmentInspection : IntentionBasedInspection( JoinDeclarationAndAssignmentIntention::class, @@ -39,6 +44,12 @@ class JoinDeclarationAndAssignmentIntention : SelfTargetingOffsetIndependentInte "Join declaration and assignment" ) { + private fun equalNullableTypes(type1: KotlinType?, type2: KotlinType?): Boolean { + if (type1 == null) return type2 == null + if (type2 == null) return false + return TypeUtils.equalTypes(type1, type2) + } + override fun isApplicableTo(element: KtProperty): Boolean { if (element.hasDelegate() || element.hasInitializer() @@ -50,7 +61,13 @@ class JoinDeclarationAndAssignmentIntention : SelfTargetingOffsetIndependentInte } val assignment = findAssignment(element) ?: return false - return assignment.right?.let { hasNoLocalDependencies(it, element.parent) } ?: false + return assignment.right?.let { + hasNoLocalDependencies(it, element.parent) && + assignment.analyze().let { context -> + (element.isVar && !element.isLocal) || + equalNullableTypes(it.getType(context), context[BindingContext.TYPE, element.typeReference]) + } + } ?: false } override fun applyTo(element: KtProperty, editor: Editor?) { @@ -60,7 +77,7 @@ class JoinDeclarationAndAssignmentIntention : SelfTargetingOffsetIndependentInte val initializer = assignment.right ?: return val newInitializer = element.setInitializer(initializer)!! - val initializerBlock = assignment.parent?.parent as? KtAnonymousInitializer + val initializerBlock = assignment.parent.parent as? KtAnonymousInitializer assignment.delete() if (initializerBlock != null && (initializerBlock.body as? KtBlockExpression)?.isEmpty() == true) { initializerBlock.delete() @@ -108,7 +125,7 @@ class JoinDeclarationAndAssignmentIntention : SelfTargetingOffsetIndependentInte if (assignments.any { it.parent.invalidParent() }) return null val first = assignments.firstOrNull() ?: return null - if (assignments.any { it !== first && it.parent?.parent is KtSecondaryConstructor}) return null + if (assignments.any { it !== first && it.parent.parent is KtSecondaryConstructor}) return null return first } diff --git a/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt b/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt new file mode 100644 index 00000000000..f60ebb38137 --- /dev/null +++ b/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME +fun foo() { + val x: String + x = System.getProperty("") +} \ No newline at end of file diff --git a/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt.after b/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt.after new file mode 100644 index 00000000000..b28369cf44e --- /dev/null +++ b/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt.after @@ -0,0 +1,5 @@ +// IS_APPLICABLE: true +// WITH_RUNTIME +fun foo() { + val x: String = System.getProperty("") +} \ No newline at end of file diff --git a/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForSmartCast.kt b/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForSmartCast.kt new file mode 100644 index 00000000000..6e9c179f8a5 --- /dev/null +++ b/idea/testData/intentions/joinDeclarationAndAssignment/assignmentForSmartCast.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo() { + val x: Any + x = "123" + x.length // Smart cast before join, error after join +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 17f3a3fa6e4..46b4443cf58 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -8754,6 +8754,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/joinDeclarationAndAssignment"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("assignmentForFlexible.kt") + public void testAssignmentForFlexible() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinDeclarationAndAssignment/assignmentForFlexible.kt"); + doTest(fileName); + } + + @TestMetadata("assignmentForSmartCast.kt") + public void testAssignmentForSmartCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinDeclarationAndAssignment/assignmentForSmartCast.kt"); + doTest(fileName); + } + @TestMetadata("assignmentInIf.kt") public void testAssignmentInIf() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/joinDeclarationAndAssignment/assignmentInIf.kt");