[NI] Fix completion for ILT when Nothing constraint is present
Full completion should not be done if lower `Nothing` is the only proper constraint when constraint with ILT type is present. ILT will be selected as a resulting type and transformed into `Int` without attention to possible restrictions from outer calls.
This commit is contained in:
Generated
+5
@@ -10662,6 +10662,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("partialForIltWithNothing.kt")
|
||||||
|
public void testPartialForIltWithNothing() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIltWithNothing.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("transitiveConstraint.kt")
|
@TestMetadata("transitiveConstraint.kt")
|
||||||
public void testTransitiveConstraint() throws Exception {
|
public void testTransitiveConstraint() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
|
||||||
|
|||||||
+28
-11
@@ -52,7 +52,7 @@ class CompletionModeCalculator {
|
|||||||
private val candidate: KotlinResolutionCandidate,
|
private val candidate: KotlinResolutionCandidate,
|
||||||
private val returnType: UnwrappedType?,
|
private val returnType: UnwrappedType?,
|
||||||
private val csCompleterContext: CsCompleterContext,
|
private val csCompleterContext: CsCompleterContext,
|
||||||
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
|
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle,
|
||||||
) {
|
) {
|
||||||
private enum class FixationDirection {
|
private enum class FixationDirection {
|
||||||
TO_SUBTYPE, EQUALITY
|
TO_SUBTYPE, EQUALITY
|
||||||
@@ -192,13 +192,32 @@ class CompletionModeCalculator {
|
|||||||
val constraints = variableWithConstraints.constraints
|
val constraints = variableWithConstraints.constraints
|
||||||
val variable = variableWithConstraints.typeVariable
|
val variable = variableWithConstraints.typeVariable
|
||||||
|
|
||||||
// todo check correctness for @Exact
|
// ILT constraint tracking is necessary to prevent incorrect full completion from Nothing constraint
|
||||||
return constraints.isNotEmpty() && constraints.any { constraint ->
|
// Consider ILT <: T; Nothing <: T for T requiring lower constraint
|
||||||
constraint.hasRequiredKind(direction)
|
// Nothing would trigger full completion, but resulting type would be Int
|
||||||
&& isProperType(constraint.type)
|
// Possible restrictions on integer constant from outer calls would be ignored
|
||||||
&& !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()
|
|
||||||
&& !isNothingConstraintForPartiallyAnalyzedVariable(constraint, variable)
|
var iltConstraintPresent = false
|
||||||
|
var properConstraintPresent = false
|
||||||
|
var nonNothingProperConstraintPresent = false
|
||||||
|
|
||||||
|
for (constraint in constraints) {
|
||||||
|
if (!constraint.hasRequiredKind(direction) || !isProperType(constraint.type))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()) {
|
||||||
|
iltConstraintPresent = true
|
||||||
|
} else if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type)) {
|
||||||
|
properConstraintPresent = true
|
||||||
|
nonNothingProperConstraintPresent = true
|
||||||
|
} else if (!isLowerConstraintForPartiallyAnalyzedVariable(constraint, variable)) {
|
||||||
|
properConstraintPresent = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!properConstraintPresent) return false
|
||||||
|
|
||||||
|
return !iltConstraintPresent || nonNothingProperConstraintPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Constraint.hasRequiredKind(direction: FixationDirection) = when (direction) {
|
private fun Constraint.hasRequiredKind(direction: FixationDirection) = when (direction) {
|
||||||
@@ -206,13 +225,11 @@ class CompletionModeCalculator {
|
|||||||
FixationDirection.EQUALITY -> kind.isEqual()
|
FixationDirection.EQUALITY -> kind.isEqual()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CsCompleterContext.isNothingConstraintForPartiallyAnalyzedVariable(
|
private fun CsCompleterContext.isLowerConstraintForPartiallyAnalyzedVariable(
|
||||||
constraint: Constraint,
|
constraint: Constraint,
|
||||||
variable: TypeVariableMarker
|
variable: TypeVariableMarker
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type) || !constraint.kind.isLower())
|
return constraint.kind.isLower() && postponedAtoms.any { atom ->
|
||||||
return false
|
|
||||||
return postponedAtoms.any { atom ->
|
|
||||||
atom.expectedType?.contains { type -> variable.defaultType() == type } ?: false
|
atom.expectedType?.contains { type -> variable.defaultType() == type } ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
// !LANGUAGE: +NewInference
|
||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||||
|
|
||||||
|
fun test(boolean: Boolean) {
|
||||||
|
val expectedLong: Long = if (boolean) {
|
||||||
|
if (boolean) {
|
||||||
|
42
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedInt: Int = if (boolean) {
|
||||||
|
if (boolean) {
|
||||||
|
42
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedShort: Short = if (boolean) {
|
||||||
|
if (boolean) {
|
||||||
|
42
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedByte: Byte = if (boolean) {
|
||||||
|
if (boolean) {
|
||||||
|
42
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
// !LANGUAGE: +NewInference
|
||||||
|
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||||
|
|
||||||
|
fun test(boolean: Boolean) {
|
||||||
|
val expectedLong: Long = if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Long")!>if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Long")!>42<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedInt: Int = if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>42<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedShort: Short = if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Short")!>if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Short")!>42<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedByte: Byte = if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Byte")!>if (boolean) {
|
||||||
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Byte")!>42<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}<!>
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(/*0*/ boolean: kotlin.Boolean): kotlin.Unit
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// !WITH_NEW_INFERENCE
|
// !WITH_NEW_INFERENCE
|
||||||
val test: Int = <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (true) {
|
val test: Int = <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (true) {
|
||||||
when (2) {
|
when (2) {
|
||||||
1 -> 1
|
1 -> 1
|
||||||
else -> <!OI;NULL_FOR_NONNULL_TYPE!>null<!>
|
else -> <!OI;NULL_FOR_NONNULL_TYPE!>null<!>
|
||||||
|
|||||||
@@ -10669,6 +10669,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("partialForIltWithNothing.kt")
|
||||||
|
public void testPartialForIltWithNothing() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIltWithNothing.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("transitiveConstraint.kt")
|
@TestMetadata("transitiveConstraint.kt")
|
||||||
public void testTransitiveConstraint() throws Exception {
|
public void testTransitiveConstraint() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
|
||||||
|
|||||||
Generated
+5
@@ -10664,6 +10664,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("partialForIltWithNothing.kt")
|
||||||
|
public void testPartialForIltWithNothing() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIltWithNothing.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("transitiveConstraint.kt")
|
@TestMetadata("transitiveConstraint.kt")
|
||||||
public void testTransitiveConstraint() throws Exception {
|
public void testTransitiveConstraint() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ abstract class AbstractFirPsiCheckerTest : AbstractPsiCheckerTest() {
|
|||||||
throw FileComparisonFailure(e.message, e.expected, e.actual, File(e.filePath).absolutePath)
|
throw FileComparisonFailure(e.message, e.expected, e.actual, File(e.filePath).absolutePath)
|
||||||
} else {
|
} else {
|
||||||
// Here we just check that we haven't crashed due to exception
|
// Here we just check that we haven't crashed due to exception
|
||||||
0L
|
0
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (configured) {
|
if (configured) {
|
||||||
|
|||||||
Reference in New Issue
Block a user