ConstantConditionIfInspection.replaceWithBranch shouldn't remove subjectVariable with side effects
Relates to #KT-30975
This commit is contained in:
@@ -17,9 +17,13 @@ import org.jetbrains.kotlin.idea.core.replaced
|
|||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.unwrapBlockOrParenthesis
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.unwrapBlockOrParenthesis
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.util.hasNoSideEffects
|
||||||
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.*
|
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
@@ -158,43 +162,29 @@ private fun KtExpression.enumEntry(): KtEnumEntry? {
|
|||||||
fun KtExpression.replaceWithBranch(branch: KtExpression, isUsedAsExpression: Boolean, keepBraces: Boolean = false) {
|
fun KtExpression.replaceWithBranch(branch: KtExpression, isUsedAsExpression: Boolean, keepBraces: Boolean = false) {
|
||||||
val caretModel = findExistingEditor()?.caretModel
|
val caretModel = findExistingEditor()?.caretModel
|
||||||
|
|
||||||
val subjectVariable = (this as? KtWhenExpression)?.subjectVariable?.let { it ->
|
val subjectVariable = (this as? KtWhenExpression)?.subjectVariable?.let(fun(property: KtProperty): KtProperty? {
|
||||||
if (it.annotationEntries.isNotEmpty()) return@let it
|
if (property.annotationEntries.isNotEmpty()) return property
|
||||||
val initializer = it.initializer ?: return@let it
|
val initializer = property.initializer ?: return property
|
||||||
val references = ReferencesSearch.search(it, LocalSearchScope(this)).toList()
|
val references = ReferencesSearch.search(property, LocalSearchScope(this)).toList()
|
||||||
when (references.size) {
|
return when (references.size) {
|
||||||
0 -> when (initializer) {
|
0 -> property.takeUnless { initializer.hasNoSideEffects() }
|
||||||
is KtSimpleNameExpression, is KtStringTemplateExpression, is KtConstantExpression -> null
|
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
1 -> {
|
1 -> {
|
||||||
references.firstOrNull()?.element?.replace(initializer)
|
if (initializer.hasNoSideEffects()) {
|
||||||
null
|
references.first().element.replace(initializer)
|
||||||
|
null
|
||||||
|
} else
|
||||||
|
property
|
||||||
}
|
}
|
||||||
else -> it
|
else -> property
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
val wrapSubjectVariableByRun = if (subjectVariable != null) {
|
|
||||||
val subjectVariableName = subjectVariable.nameAsName
|
|
||||||
val parentBlock = getStrictParentOfType<KtBlockExpression>()
|
|
||||||
parentBlock?.anyDescendantOfType<KtProperty> { it != subjectVariable && it.nameAsName == subjectVariableName } == true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
val factory = KtPsiFactory(this)
|
val factory = KtPsiFactory(this)
|
||||||
val parent = this.parent
|
val parent = this.parent
|
||||||
val replaced = when {
|
val replaced = when {
|
||||||
branch !is KtBlockExpression -> {
|
branch !is KtBlockExpression -> {
|
||||||
if (subjectVariable != null) {
|
if (subjectVariable != null) {
|
||||||
if (isUsedAsExpression || wrapSubjectVariableByRun) {
|
replaced(KtPsiFactory(this).createExpressionByPattern("run { $0\n$1 }", subjectVariable, branch))
|
||||||
replaced(KtPsiFactory(this).createExpressionByPattern("run { $0\n$1 }", subjectVariable, branch))
|
|
||||||
} else {
|
|
||||||
parent.addBefore(subjectVariable, this).also {
|
|
||||||
parent.addAfter(factory.createNewLine(), it)
|
|
||||||
replaced(branch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
replaced(branch)
|
replaced(branch)
|
||||||
}
|
}
|
||||||
@@ -212,12 +202,11 @@ fun KtExpression.replaceWithBranch(branch: KtExpression, isUsedAsExpression: Boo
|
|||||||
if (keepBraces) {
|
if (keepBraces) {
|
||||||
parent.addAfter(branch, this)
|
parent.addAfter(branch, this)
|
||||||
} else {
|
} else {
|
||||||
if (subjectVariable != null && wrapSubjectVariableByRun) {
|
if (subjectVariable != null) {
|
||||||
branch.addAfter(subjectVariable, branch.lBrace)
|
branch.addAfter(subjectVariable, branch.lBrace)
|
||||||
parent.addAfter(KtPsiFactory(this).createExpression("run ${branch.text}"), this)
|
parent.addAfter(KtPsiFactory(this).createExpression("run ${branch.text}"), this)
|
||||||
} else {
|
} else {
|
||||||
parent.addRangeAfter(firstChildSibling, lastChild.prevSibling, this)
|
parent.addRangeAfter(firstChildSibling, lastChild.prevSibling, this)
|
||||||
subjectVariable?.let { parent.addBefore(subjectVariable, this) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.util
|
package org.jetbrains.kotlin.idea.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
|
||||||
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
|
|
||||||
import org.jetbrains.kotlin.psi.KtTypeArgumentList
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
|
|
||||||
fun KtCallExpression.replaceOrCreateTypeArgumentList(newTypeArgumentList: KtTypeArgumentList) {
|
fun KtCallExpression.replaceOrCreateTypeArgumentList(newTypeArgumentList: KtTypeArgumentList) {
|
||||||
@@ -25,4 +25,11 @@ fun KtModifierListOwner.hasInlineModifier() = hasModifier(KtTokens.INLINE_KEYWOR
|
|||||||
|
|
||||||
fun KtPrimaryConstructor.allowedValOrVar(): Boolean = containingClass()?.let {
|
fun KtPrimaryConstructor.allowedValOrVar(): Boolean = containingClass()?.let {
|
||||||
it.isAnnotation() || it.hasInlineModifier()
|
it.isAnnotation() || it.hasInlineModifier()
|
||||||
} ?: false
|
} ?: false
|
||||||
|
|
||||||
|
// TODO: add cases
|
||||||
|
fun KtExpression.hasNoSideEffects(): Boolean = when (this) {
|
||||||
|
is KtStringTemplateExpression -> !hasInterpolation()
|
||||||
|
is KtConstantExpression -> true
|
||||||
|
else -> ConstantExpressionEvaluator.getConstant(this, analyze(BodyResolveMode.PARTIAL)) != null
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
fun println(s: String) {}
|
fun println(s: String) {}
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
<caret>println("")
|
println("")
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = create()) {
|
when<caret> (val a = create()) {
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
Vendored
+6
-3
@@ -1,7 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>val a = create()
|
run {
|
||||||
use(a, a)
|
val a = create()
|
||||||
foo()
|
use(a, a)
|
||||||
|
foo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = create()) {
|
when<caret> (val a = create()) {
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
Vendored
+6
-3
@@ -1,7 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>val a = create()
|
run {
|
||||||
use("")
|
val a = create()
|
||||||
foo()
|
use("")
|
||||||
|
foo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = 42) {
|
when<caret> (val a = 42) {
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
Vendored
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>use("")
|
use("")
|
||||||
foo()
|
foo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = create()) {
|
when<caret> (val a = create()) {
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
Vendored
+6
-2
@@ -1,6 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>use(create())
|
run {
|
||||||
foo()
|
val a = create()
|
||||||
|
use(a)
|
||||||
|
foo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
val x = <caret>run {
|
val x = run {
|
||||||
use(create())
|
val a = create()
|
||||||
|
use(a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun hasAnnotation() {
|
fun hasAnnotation() {
|
||||||
when<caret> (@Bar val a = create()) {
|
when<caret> (@Bar val a = create()) {
|
||||||
else -> use(a)
|
else -> use(a)
|
||||||
|
|||||||
+5
-2
@@ -1,6 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun hasAnnotation() {
|
fun hasAnnotation() {
|
||||||
<caret>@Bar val a = create()
|
run {
|
||||||
use(a)
|
@Bar val a = create()
|
||||||
|
use(a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = create()) {
|
when<caret> (val a = create()) {
|
||||||
else -> use(a, a)
|
else -> use(a, a)
|
||||||
|
|||||||
Vendored
+5
-2
@@ -1,6 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>val a = create()
|
run {
|
||||||
use(a, a)
|
val a = create()
|
||||||
|
use(a, a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = create()) {
|
when<caret> (val a = create()) {
|
||||||
else -> use("")
|
else -> use("")
|
||||||
|
|||||||
Vendored
+5
-2
@@ -1,6 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>val a = create()
|
run {
|
||||||
use("")
|
val a = create()
|
||||||
|
use("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun test() {
|
fun test() {
|
||||||
<caret>use("")
|
use("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun use(s: String) {}
|
fun use(s: String) {}
|
||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
when<caret> (val a = create()) {
|
when<caret> (val a = create()) {
|
||||||
else -> use(a)
|
else -> use(a)
|
||||||
|
|||||||
Vendored
+5
-1
@@ -1,5 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
<caret>use(create())
|
run {
|
||||||
|
val a = create()
|
||||||
|
use(a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,9 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
fun test() {
|
fun test() {
|
||||||
val x = <caret>use(create())
|
val x = run {
|
||||||
|
val a = create()
|
||||||
|
use(a)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun create(): String = ""
|
fun create(): String = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user