[FE 1.0] Don't lose diagnostics during lambda analysis at the overload resolution by return type stage

^KT-49658 Fixed
This commit is contained in:
Victor Petukhov
2022-01-11 15:22:03 +03:00
committed by teamcity
parent 7820b268fb
commit 37d163d417
17 changed files with 212 additions and 21 deletions
@@ -13647,6 +13647,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/inference/kt47316.kt");
}
@Test
@TestMetadata("kt49658.kt")
public void testKt49658() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658.kt");
}
@Test
@TestMetadata("kt49658Strict.kt")
public void testKt49658Strict() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658Strict.kt");
}
@Test
@TestMetadata("kt6175.kt")
public void testKt6175() throws Exception {
@@ -13647,6 +13647,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inference/kt47316.kt");
}
@Test
@TestMetadata("kt49658.kt")
public void testKt49658() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658.kt");
}
@Test
@TestMetadata("kt49658Strict.kt")
public void testKt49658Strict() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658Strict.kt");
}
@Test
@TestMetadata("kt6175.kt")
public void testKt6175() throws Exception {
@@ -13647,6 +13647,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inference/kt47316.kt");
}
@Test
@TestMetadata("kt49658.kt")
public void testKt49658() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658.kt");
}
@Test
@TestMetadata("kt49658Strict.kt")
public void testKt49658Strict() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658Strict.kt");
}
@Test
@TestMetadata("kt6175.kt")
public void testKt6175() throws Exception {
@@ -1103,6 +1103,7 @@ public interface Errors {
DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<KtElement, KtElement> ILLEGAL_ESCAPE = DiagnosticFactory1.create(ERROR, CUT_CHAR_QUOTES);
DiagnosticFactory1<KtConstantExpression, KotlinType> NULL_FOR_NONNULL_TYPE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<KtConstantExpression, KotlinType> NULL_FOR_NONNULL_TYPE_WARNING = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<KtEscapeStringTemplateEntry> ILLEGAL_ESCAPE_SEQUENCE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtConstantExpression> UNSIGNED_LITERAL_WITHOUT_DECLARATIONS_ON_CLASSPATH = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> SIGNED_CONSTANT_CONVERTED_TO_UNSIGNED = DiagnosticFactory0.create(ERROR);
@@ -632,6 +632,7 @@ public class DefaultErrorMessages {
MAP.put(TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL, "Too many characters in a character literal ''{0}''", ELEMENT_TEXT);
MAP.put(ILLEGAL_ESCAPE, "Illegal escape: ''{0}''", ELEMENT_TEXT);
MAP.put(NULL_FOR_NONNULL_TYPE, "Null can not be a value of a non-null type {0}", RENDER_TYPE);
MAP.put(NULL_FOR_NONNULL_TYPE_WARNING, "Null can not be a value of a non-null type {0}", RENDER_TYPE);
MAP.put(ELSE_MISPLACED_IN_WHEN, "'else' entry must be the last one in a when-expression");
MAP.put(REDUNDANT_ELSE_IN_WHEN, "'when' is exhaustive so 'else' is redundant here");
@@ -215,18 +215,14 @@ class DiagnosticReporterByTrackingStrategy(
}
}
ArgumentNullabilityMismatchDiagnostic::class.java -> {
require(diagnostic is ArgumentNullabilityMismatchDiagnostic)
val expression = callArgument.safeAs<PSIKotlinCallArgument>()?.valueArgument?.getArgumentExpression()?.let {
KtPsiUtil.deparenthesize(it) ?: it
}
if (expression != null) {
if (expression.isNull() && expression is KtConstantExpression) {
trace.reportDiagnosticOnce(NULL_FOR_NONNULL_TYPE.on(expression, diagnostic.expectedType))
} else {
trace.report(TYPE_MISMATCH.on(expression, diagnostic.expectedType, diagnostic.actualType))
}
}
ArgumentNullabilityErrorDiagnostic::class.java -> {
require(diagnostic is ArgumentNullabilityErrorDiagnostic)
reportNullabilityMismatchDiagnostic(callArgument, diagnostic)
}
ArgumentNullabilityWarningDiagnostic::class.java -> {
require(diagnostic is ArgumentNullabilityWarningDiagnostic)
reportNullabilityMismatchDiagnostic(callArgument, diagnostic)
}
CallableReferencesDefaultArgumentUsed::class.java -> {
@@ -582,6 +578,27 @@ class DiagnosticReporterByTrackingStrategy(
}
}
private fun reportNullabilityMismatchDiagnostic(callArgument: KotlinCallArgument, diagnostic: ArgumentNullabilityMismatchDiagnostic) {
val expression = callArgument.safeAs<PSIKotlinCallArgument>()?.valueArgument?.getArgumentExpression()?.let {
KtPsiUtil.deparenthesize(it) ?: it
}
if (expression != null) {
if (expression.isNull() && expression is KtConstantExpression) {
val factory = when (diagnostic) {
is ArgumentNullabilityErrorDiagnostic -> NULL_FOR_NONNULL_TYPE
is ArgumentNullabilityWarningDiagnostic -> NULL_FOR_NONNULL_TYPE_WARNING
}
trace.reportDiagnosticOnce(factory.on(expression, diagnostic.expectedType))
} else {
val factory = when (diagnostic) {
is ArgumentNullabilityErrorDiagnostic -> TYPE_MISMATCH
is ArgumentNullabilityWarningDiagnostic -> TYPE_MISMATCH_WARNING
}
trace.report(factory.on(expression, diagnostic.expectedType, diagnostic.actualType))
}
}
}
private fun reportNotEnoughInformationForTypeParameterForSpecialCall(
resolvedAtom: ResolvedCallAtom,
error: NotEnoughInformationForTypeParameterImpl
@@ -622,7 +639,6 @@ class DiagnosticReporterByTrackingStrategy(
|| it.constructor == uninferredTypeVariable.freshTypeConstructor(typeSystemContext)
}
@OptIn(ExperimentalStdlibApi::class)
private fun getSubResolvedAtomsOfSpecialCallToReportUninferredTypeParameter(
resolvedAtom: ResolvedAtom,
uninferredTypeVariable: TypeVariableMarker
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor
import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
@@ -31,7 +32,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.same
class KotlinCallCompleter(
private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
private val kotlinConstraintSystemCompleter: KotlinConstraintSystemCompleter,
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle,
private val languageVersionSettings: LanguageVersionSettings
) {
fun runCompletion(
@@ -127,6 +129,7 @@ class KotlinCallCompleter(
ConstraintSystemCompletionMode.FULL,
diagnosticHolderForLambda,
)
propagateLambdaAnalysisDiagnostics(diagnosticHolderForLambda, firstCandidate)
while (iterator.hasNext()) {
val (candidate, atom) = iterator.next()
@@ -137,6 +140,7 @@ class KotlinCallCompleter(
ConstraintSystemCompletionMode.FULL,
diagnosticHolderForLambda
)
propagateLambdaAnalysisDiagnostics(diagnosticHolderForLambda, candidate)
}
val errorCandidates = mutableSetOf<SimpleResolutionCandidate>()
@@ -155,6 +159,24 @@ class KotlinCallCompleter(
}
}
private fun propagateLambdaAnalysisDiagnostics(
diagnosticHolder: KotlinDiagnosticsHolder.SimpleHolder,
candidate: SimpleResolutionCandidate
) {
val dontLoseDiagnosticsDuringOverloadResolutionByReturnType =
languageVersionSettings.supportsFeature(LanguageFeature.DontLoseDiagnosticsDuringOverloadResolutionByReturnType)
for (diagnostic in diagnosticHolder.getDiagnostics()) {
if (diagnostic is TransformableToWarning<*>) {
val transformedDiagnostic =
if (dontLoseDiagnosticsDuringOverloadResolutionByReturnType) diagnostic else diagnostic.transformToWarning()
if (transformedDiagnostic != null) {
candidate.addDiagnostic(transformedDiagnostic)
}
}
}
}
private fun SimpleResolutionCandidate.getInputTypesOfLambdaAtom(atom: ResolvedLambdaAtom): List<UnwrappedType> {
val result = mutableListOf<UnwrappedType>()
val substitutor = getSystem().getBuilder().buildCurrentSubstitutor()
@@ -85,7 +85,7 @@ private fun checkExpressionArgument(
if (argumentType.isMarkedNullable) {
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType.makeNotNullable(), actualExpectedType, position)) {
return ArgumentNullabilityMismatchDiagnostic(actualExpectedType, argumentType, expressionArgument)
return ArgumentNullabilityErrorDiagnostic(actualExpectedType, argumentType, expressionArgument)
}
}
@@ -97,7 +97,7 @@ private fun checkExpressionArgument(
// Used only for arguments with @NotNull annotation
if (expectedType is NotNullTypeParameter && argumentType.isMarkedNullable) {
diagnosticsHolder.addDiagnostic(ArgumentNullabilityMismatchDiagnostic(expectedType, argumentType, expressionArgument))
diagnosticsHolder.addDiagnostic(ArgumentNullabilityErrorDiagnostic(expectedType, argumentType, expressionArgument))
}
if (expressionArgument.isSafeCall) {
@@ -23,12 +23,18 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.components.candidate.CallableReferenceResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintError
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintWarning
import org.jetbrains.kotlin.resolve.calls.inference.model.transformToWarning
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability.*
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.UnwrappedType
interface TransformableToWarning<T : KotlinCallDiagnostic> {
fun transformToWarning(): T?
}
abstract class InapplicableArgumentDiagnostic : KotlinCallDiagnostic(INAPPLICABLE) {
abstract val argument: KotlinCallArgument
@@ -227,11 +233,29 @@ class NonApplicableCallForBuilderInferenceDiagnostic(val kotlinCall: KotlinCall)
}
}
class ArgumentNullabilityMismatchDiagnostic(
val expectedType: UnwrappedType,
val actualType: UnwrappedType,
sealed interface ArgumentNullabilityMismatchDiagnostic {
val expectedType: UnwrappedType
val actualType: UnwrappedType
val expressionArgument: ExpressionKotlinCallArgument
) : KotlinCallDiagnostic(UNSAFE_CALL) {
}
class ArgumentNullabilityErrorDiagnostic(
override val expectedType: UnwrappedType,
override val actualType: UnwrappedType,
override val expressionArgument: ExpressionKotlinCallArgument
) : KotlinCallDiagnostic(UNSAFE_CALL), TransformableToWarning<ArgumentNullabilityWarningDiagnostic>, ArgumentNullabilityMismatchDiagnostic {
override fun report(reporter: DiagnosticReporter) {
reporter.onCallArgument(expressionArgument, this)
}
override fun transformToWarning() = ArgumentNullabilityWarningDiagnostic(expectedType, actualType, expressionArgument)
}
class ArgumentNullabilityWarningDiagnostic(
override val expectedType: UnwrappedType,
override val actualType: UnwrappedType,
override val expressionArgument: ExpressionKotlinCallArgument
) : KotlinCallDiagnostic(RESOLVED), ArgumentNullabilityMismatchDiagnostic {
override fun report(reporter: DiagnosticReporter) {
reporter.onCallArgument(expressionArgument, this)
}
@@ -287,8 +311,11 @@ class MultipleArgumentsApplicableForContextReceiver(val receiverDescriptor: Rece
class KotlinConstraintSystemDiagnostic(
val error: ConstraintSystemError
) : KotlinCallDiagnostic(error.applicability) {
) : KotlinCallDiagnostic(error.applicability), TransformableToWarning<KotlinConstraintSystemDiagnostic> {
override fun report(reporter: DiagnosticReporter) = reporter.constraintError(error)
override fun transformToWarning(): KotlinConstraintSystemDiagnostic? =
if (error is NewConstraintError) KotlinConstraintSystemDiagnostic(error.transformToWarning()) else null
}
val KotlinCallDiagnostic.constraintSystemError: ConstraintSystemError?
@@ -0,0 +1,16 @@
// WITH_STDLIB
fun doTheMapThing1(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!ARGUMENT_TYPE_MISMATCH!>when (it) { // NullPointerException
is String -> listOf("Yeah")
else -> null
}<!>
}
}
fun doTheMapThing2(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!ARGUMENT_TYPE_MISMATCH!>if (it is String) listOf("Yeah") else null<!> // it's OK with `if`
}
}
@@ -0,0 +1,16 @@
// WITH_STDLIB
fun doTheMapThing1(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!TYPE_MISMATCH_WARNING!>when (it) { // NullPointerException
is String -> listOf("Yeah")
else -> null
}<!>
}
}
fun doTheMapThing2(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH_WARNING!>if (it is String) listOf("Yeah") else null<!> // it's OK with `if`
}
}
@@ -0,0 +1,5 @@
package
public fun doTheMapThing1(/*0*/ elements: kotlin.collections.List<kotlin.CharSequence>): kotlin.collections.List<kotlin.String>
public fun doTheMapThing2(/*0*/ elements: kotlin.collections.List<kotlin.CharSequence>): kotlin.collections.List<kotlin.String>
@@ -0,0 +1,17 @@
// !LANGUAGE: +DontLoseDiagnosticsDuringOverloadResolutionByReturnType
// WITH_STDLIB
fun doTheMapThing1(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!ARGUMENT_TYPE_MISMATCH!>when (it) { // NullPointerException
is String -> listOf("Yeah")
else -> null
}<!>
}
}
fun doTheMapThing2(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!ARGUMENT_TYPE_MISMATCH!>if (it is String) listOf("Yeah") else null<!> // it's OK with `if`
}
}
@@ -0,0 +1,17 @@
// !LANGUAGE: +DontLoseDiagnosticsDuringOverloadResolutionByReturnType
// WITH_STDLIB
fun doTheMapThing1(elements: List<CharSequence>): List<String> {
return elements.<!CANDIDATE_CHOSEN_USING_OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION!>flatMap {
<!TYPE_MISMATCH!>when (it) { // NullPointerException
is String -> listOf("Yeah")
else -> null
}<!>
}<!>
}
fun doTheMapThing2(elements: List<CharSequence>): List<String> {
return elements.<!CANDIDATE_CHOSEN_USING_OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION!>flatMap {
<!TYPE_MISMATCH, TYPE_MISMATCH, TYPE_MISMATCH!>if (it is String) listOf("Yeah") else null<!> // it's OK with `if`
}<!>
}
@@ -0,0 +1,4 @@
package
public fun doTheMapThing1(/*0*/ elements: kotlin.collections.List<kotlin.CharSequence>): kotlin.collections.List<kotlin.String>
public fun doTheMapThing2(/*0*/ elements: kotlin.collections.List<kotlin.CharSequence>): kotlin.collections.List<kotlin.String>
@@ -13653,6 +13653,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inference/kt47316.kt");
}
@Test
@TestMetadata("kt49658.kt")
public void testKt49658() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658.kt");
}
@Test
@TestMetadata("kt49658Strict.kt")
public void testKt49658Strict() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt49658Strict.kt");
}
@Test
@TestMetadata("kt6175.kt")
public void testKt6175() throws Exception {
@@ -249,6 +249,7 @@ enum class LanguageFeature(
// 1.8
DontLoseDiagnosticsDuringOverloadResolutionByReturnType(KOTLIN_1_8),
ProhibitConfusingSyntaxInWhenBranches(KOTLIN_1_8, kind = BUG_FIX), // KT-48385
UseConsistentRulesForPrivateConstructorsOfSealedClasses(sinceVersion = KOTLIN_1_8, kind = BUG_FIX), // KT-44866
ProgressionsChangingResolve(KOTLIN_1_8), // KT-49276