Honor errors in code before when checking smart casts
This commit is contained in:
@@ -18,9 +18,11 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
|||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.UserDataHolderBase
|
import com.intellij.openapi.util.UserDataHolderBase
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
@@ -244,10 +246,19 @@ fun <TExpression : KtExpression> tryChangeAndCheckErrors(
|
|||||||
// we declare these keys locally to avoid possible race-condition problems if this code is executed in 2 threads simultaneously
|
// we declare these keys locally to avoid possible race-condition problems if this code is executed in 2 threads simultaneously
|
||||||
val EXPRESSION = Key<Unit>("EXPRESSION")
|
val EXPRESSION = Key<Unit>("EXPRESSION")
|
||||||
val SCOPE_TO_EXCLUDE = Key<Unit>("SCOPE_TO_EXCLUDE")
|
val SCOPE_TO_EXCLUDE = Key<Unit>("SCOPE_TO_EXCLUDE")
|
||||||
|
val ERRORS_BEFORE = Key<Collection<DiagnosticFactory<*>>>("ERRORS_BEFORE")
|
||||||
|
|
||||||
expressionToChange.putCopyableUserData(EXPRESSION, Unit)
|
expressionToChange.putCopyableUserData(EXPRESSION, Unit)
|
||||||
scopeToExclude?.putCopyableUserData(SCOPE_TO_EXCLUDE, Unit)
|
scopeToExclude?.putCopyableUserData(SCOPE_TO_EXCLUDE, Unit)
|
||||||
|
|
||||||
|
block.forEachDescendantOfType<PsiElement> { element ->
|
||||||
|
val errors = bindingContext.diagnostics.forElement(element)
|
||||||
|
.filter { it.severity == Severity.ERROR }
|
||||||
|
if (errors.isNotEmpty()) {
|
||||||
|
element.putCopyableUserData(ERRORS_BEFORE, errors.map { it.factory })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val blockCopy = block.copied()
|
val blockCopy = block.copied()
|
||||||
val expressionCopy: TExpression
|
val expressionCopy: TExpression
|
||||||
val scopeToExcludeCopy: KtElement?
|
val scopeToExcludeCopy: KtElement?
|
||||||
@@ -268,8 +279,11 @@ fun <TExpression : KtExpression> tryChangeAndCheckErrors(
|
|||||||
contextExpression = block,
|
contextExpression = block,
|
||||||
dataFlowInfo = bindingContext.getDataFlowInfo(block),
|
dataFlowInfo = bindingContext.getDataFlowInfo(block),
|
||||||
trace = DelegatingBindingTrace(bindingContext, "Temporary trace"))
|
trace = DelegatingBindingTrace(bindingContext, "Temporary trace"))
|
||||||
//TODO: what if there were errors before?
|
return newBindingContext.diagnostics.none {
|
||||||
return newBindingContext.diagnostics.none { it.severity == Severity.ERROR && !scopeToExcludeCopy.isAncestor(it.psiElement) }
|
it.severity == Severity.ERROR
|
||||||
|
&& !scopeToExcludeCopy.isAncestor(it.psiElement)
|
||||||
|
&& it.factory !in (it.psiElement.getCopyableUserData(ERRORS_BEFORE) ?: emptyList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val NO_SIDE_EFFECT_STANDARD_CLASSES = setOf(
|
private val NO_SIDE_EFFECT_STANDARD_CLASSES = setOf(
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// ERROR: Unresolved reference: unresolvedFun
|
||||||
|
// INTENTION_TEXT: "Replace with 'map{}.map{}.firstOrNull{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.map{}.firstOrNull{}'"
|
||||||
|
fun foo(list: List<String>, o: Any) {
|
||||||
|
if (o is CharSequence) {
|
||||||
|
unresolvedFun()
|
||||||
|
|
||||||
|
var result: Any? = null
|
||||||
|
<caret>for (s in list) {
|
||||||
|
val a = s.length + (o as String).capitalize().hashCode()
|
||||||
|
val x = a * o.length
|
||||||
|
if (x > 1000) {
|
||||||
|
result = x
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// ERROR: Unresolved reference: unresolvedFun
|
||||||
|
// INTENTION_TEXT: "Replace with 'map{}.map{}.firstOrNull{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.map{}.firstOrNull{}'"
|
||||||
|
fun foo(list: List<String>, o: Any) {
|
||||||
|
if (o is CharSequence) {
|
||||||
|
unresolvedFun()
|
||||||
|
|
||||||
|
val result: Any? = list
|
||||||
|
.map { it.length + (o as String).capitalize().hashCode() }
|
||||||
|
.map { it * o.length }
|
||||||
|
.firstOrNull { it > 1000 }
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// ERROR: Unresolved reference: unresolvedFun
|
||||||
|
// INTENTION_TEXT: "Replace with 'map{}.map{}.firstOrNull{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.map{}.firstOrNull{}'"
|
||||||
|
fun foo(list: List<String>, o: Any) {
|
||||||
|
if (o is CharSequence) {
|
||||||
|
unresolvedFun()
|
||||||
|
|
||||||
|
val result: Any? = list
|
||||||
|
.asSequence()
|
||||||
|
.map { it.length + (o as String).capitalize().hashCode() }
|
||||||
|
.map { it * o.length }
|
||||||
|
.firstOrNull { it > 1000 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1168,6 +1168,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/loopToCallChain/smartCasts"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/loopToCallChain/smartCasts"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("errorOutsideLoop.kt")
|
||||||
|
public void testErrorOutsideLoop() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/smartCasts/errorOutsideLoop.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("smartCastNotBroken.kt")
|
@TestMetadata("smartCastNotBroken.kt")
|
||||||
public void testSmartCastNotBroken() throws Exception {
|
public void testSmartCastNotBroken() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/smartCasts/smartCastNotBroken.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/smartCasts/smartCastNotBroken.kt");
|
||||||
|
|||||||
@@ -9076,6 +9076,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/loopToCallChain/smartCasts"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/loopToCallChain/smartCasts"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("errorOutsideLoop.kt")
|
||||||
|
public void testErrorOutsideLoop() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/smartCasts/errorOutsideLoop.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("smartCastNotBroken.kt")
|
@TestMetadata("smartCastNotBroken.kt")
|
||||||
public void testSmartCastNotBroken() throws Exception {
|
public void testSmartCastNotBroken() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/smartCasts/smartCastNotBroken.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/smartCasts/smartCastNotBroken.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user