[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:
Pavel Kirpichenkov
2020-02-14 13:37:12 +03:00
parent 208c06516b
commit 4a7b4d655c
9 changed files with 136 additions and 13 deletions
@@ -52,7 +52,7 @@ class CompletionModeCalculator {
private val candidate: KotlinResolutionCandidate,
private val returnType: UnwrappedType?,
private val csCompleterContext: CsCompleterContext,
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle,
) {
private enum class FixationDirection {
TO_SUBTYPE, EQUALITY
@@ -192,13 +192,32 @@ class CompletionModeCalculator {
val constraints = variableWithConstraints.constraints
val variable = variableWithConstraints.typeVariable
// todo check correctness for @Exact
return constraints.isNotEmpty() && constraints.any { constraint ->
constraint.hasRequiredKind(direction)
&& isProperType(constraint.type)
&& !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()
&& !isNothingConstraintForPartiallyAnalyzedVariable(constraint, variable)
// ILT constraint tracking is necessary to prevent incorrect full completion from Nothing constraint
// Consider ILT <: T; Nothing <: T for T requiring lower constraint
// Nothing would trigger full completion, but resulting type would be Int
// Possible restrictions on integer constant from outer calls would be ignored
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) {
@@ -206,13 +225,11 @@ class CompletionModeCalculator {
FixationDirection.EQUALITY -> kind.isEqual()
}
private fun CsCompleterContext.isNothingConstraintForPartiallyAnalyzedVariable(
private fun CsCompleterContext.isLowerConstraintForPartiallyAnalyzedVariable(
constraint: Constraint,
variable: TypeVariableMarker
): Boolean {
if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type) || !constraint.kind.isLower())
return false
return postponedAtoms.any { atom ->
return constraint.kind.isLower() && postponedAtoms.any { atom ->
atom.expectedType?.contains { type -> variable.defaultType() == type } ?: false
}
}