K2: report MISSING_DEPENDENCY_CLASS for lambda parameters if needed

#KT-62525 Fixed
This commit is contained in:
Mikhail Glukhikh
2023-12-01 15:20:28 +01:00
committed by Space Team
parent c03830556f
commit 06ce57ea56
23 changed files with 325 additions and 41 deletions
@@ -22742,6 +22742,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/javac/Annotations.kt");
}
@Test
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/Lambda.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -22742,6 +22742,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/javac/Annotations.kt");
}
@Test
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/Lambda.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -22736,6 +22736,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/javac/Annotations.kt");
}
@Test
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/Lambda.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -22742,6 +22742,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/javac/Annotations.kt");
}
@Test
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/Lambda.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -199,6 +199,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val valueParameterCheckers: Set<FirValueParameterChecker>
get() = setOf(
FirValueParameterDefaultValueTypeMismatchChecker,
FirMissingDependencyClassForParameterChecker,
)
override val enumEntryCheckers: Set<FirEnumEntryChecker>
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirMissingDependencyClassProxy
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
object FirMissingDependencyClassForParameterChecker : FirValueParameterChecker(), FirMissingDependencyClassProxy {
override fun check(
declaration: FirValueParameter,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val missingTypes = mutableSetOf<ConeKotlinType>()
considerType(declaration.returnTypeRef.coneType, missingTypes, context)
reportMissingTypes(declaration.source, missingTypes, context, reporter)
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
@@ -20,7 +21,7 @@ import org.jetbrains.kotlin.fir.types.*
/**
* @see org.jetbrains.kotlin.resolve.checkers.MissingDependencyClassChecker
*/
object FirMissingDependencyClassChecker : FirQualifiedAccessExpressionChecker() {
object FirMissingDependencyClassChecker : FirQualifiedAccessExpressionChecker(), FirMissingDependencyClassProxy {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val calleeReference = expression.calleeReference
@@ -29,33 +30,21 @@ object FirMissingDependencyClassChecker : FirQualifiedAccessExpressionChecker()
if (calleeReference.isError() && calleeReference.diagnostic !is ConeDiagnosticWithSingleCandidate) return
val missingTypes = mutableSetOf<ConeKotlinType>()
fun consider(type: ConeKotlinType) {
var hasError = false
var hasMissingClass = false
type.forEachClassLikeType {
when (it) {
is ConeErrorType -> hasError = true
else -> hasMissingClass = hasMissingClass || it.lookupTag.toSymbol(context.session) == null
}
}
if (hasMissingClass && !hasError) {
val reportedType = type.withNullability(ConeNullability.NOT_NULL, context.session.typeContext).withArguments(emptyArray())
missingTypes.add(reportedType)
}
}
val symbol = calleeReference.toResolvedCallableSymbol() ?: return
consider(symbol.resolvedReturnTypeRef.coneType)
symbol.resolvedReceiverTypeRef?.coneType?.let(::consider)
(symbol as? FirFunctionSymbol<*>)?.valueParameterSymbols?.forEach { consider(it.resolvedReturnTypeRef.coneType) }
for (missingType in missingTypes) {
reporter.reportOn(expression.source, FirErrors.MISSING_DEPENDENCY_CLASS, missingType, context)
considerType(symbol.resolvedReturnTypeRef.coneType, missingTypes, context)
symbol.resolvedReceiverTypeRef?.coneType?.let {
considerType(it, missingTypes, context)
}
(symbol as? FirFunctionSymbol<*>)?.valueParameterSymbols?.forEach {
considerType(it.resolvedReturnTypeRef.coneType, missingTypes, context)
}
reportMissingTypes(expression.source, missingTypes, context, reporter)
}
}
private fun ConeKotlinType.forEachClassLikeType(action: (ConeClassLikeType) -> Unit) {
internal interface FirMissingDependencyClassProxy {
fun ConeKotlinType.forEachClassLikeType(action: (ConeClassLikeType) -> Unit) {
when (this) {
is ConeFlexibleType -> {
lowerBound.forEachClassLikeType(action)
@@ -68,4 +57,38 @@ object FirMissingDependencyClassChecker : FirQualifiedAccessExpressionChecker()
else -> {} // Ignore all type parameters.
}
}
fun considerType(type: ConeKotlinType, missingTypes: MutableSet<ConeKotlinType>, context: CheckerContext) {
var hasError = false
var hasMissingClass = false
type.forEachClassLikeType {
when (it) {
is ConeErrorType -> {
val delegatedType = it.delegatedType
if (delegatedType == null) {
hasError = true
} else {
considerType(delegatedType, missingTypes, context)
}
}
else -> hasMissingClass = hasMissingClass || it.lookupTag.toSymbol(context.session) == null
}
}
if (hasMissingClass && !hasError) {
val reportedType = type.withNullability(ConeNullability.NOT_NULL, context.session.typeContext).withArguments(emptyArray())
missingTypes.add(reportedType)
}
}
fun reportMissingTypes(
source: KtSourceElement?,
missingTypes: MutableSet<ConeKotlinType>,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
for (missingType in missingTypes) {
reporter.reportOn(source, FirErrors.MISSING_DEPENDENCY_CLASS, missingType, context)
}
}
}
@@ -44,6 +44,7 @@ class ConeClassLikeErrorLookupTag(override val classId: ClassId) : ConeClassLike
class ConeErrorType(
val diagnostic: ConeDiagnostic,
val isUninferredParameter: Boolean = false,
val delegatedType: ConeKotlinType? = null,
override val typeArguments: Array<out ConeTypeProjection> = EMPTY_ARRAY,
override val attributes: ConeAttributes = ConeAttributes.Empty
) : ConeClassLikeType() {
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker
class TypeApproximatorForMetadataSerializer(session: FirSession) :
AbstractTypeApproximator(session.typeContext, session.languageVersionSettings) {
override fun createErrorType(debugName: String): SimpleTypeMarker {
override fun createErrorType(debugName: String, delegatedType: SimpleTypeMarker?): SimpleTypeMarker {
return ConeErrorType(ConeIntermediateDiagnostic(debugName))
}
}
@@ -187,8 +187,8 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
is ConeErrorType -> ConeErrorType(
diagnostic,
isUninferredParameter,
newArguments as Array<ConeTypeProjection>,
attributes
typeArguments = newArguments as Array<ConeTypeProjection>,
attributes = attributes
)
else -> errorWithAttachment("Unknown class-like type to substitute, ${this::class}") {
withConeTypeEntry("type", this@substituteArguments)
@@ -384,8 +384,8 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return isContainedInInvariantOrContravariantPositions
}
override fun createErrorType(debugName: String): ConeErrorType {
return ConeErrorType(ConeIntermediateDiagnostic(debugName))
override fun createErrorType(debugName: String, delegatedType: SimpleTypeMarker?): ConeErrorType {
return ConeErrorType(ConeIntermediateDiagnostic(debugName), delegatedType = delegatedType as ConeKotlinType?)
}
override fun createUninferredType(constructor: TypeConstructorMarker): KotlinTypeMarker {
@@ -434,9 +434,9 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
}
override fun TypeConstructorMarker.toErrorType(): SimpleTypeMarker {
if (this is ErrorTypeConstructor) return createErrorType(reason)
if (this is ConeClassLikeLookupTag) return createErrorType("Not found classifier: $classId")
return createErrorType("Unknown reason")
if (this is ErrorTypeConstructor) return createErrorType(reason, delegatedType = null)
if (this is ConeClassLikeLookupTag) return createErrorType("Not found classifier: $classId", delegatedType = null)
return createErrorType("Unknown reason", delegatedType = null)
}
override fun findCommonIntegerLiteralTypesSuperType(explicitSupertypes: List<SimpleTypeMarker>): SimpleTypeMarker? {
@@ -209,7 +209,7 @@ fun <T : ConeKotlinType> T.withArguments(arguments: Array<out ConeTypeProjection
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeErrorType -> ConeErrorType(diagnostic, isUninferredParameter, arguments, attributes) as T
is ConeErrorType -> ConeErrorType(diagnostic, isUninferredParameter, typeArguments = arguments, attributes = attributes) as T
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, arguments, nullability.isNullable, attributes) as T
is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.withArguments(arguments)) as T
else -> errorWithAttachment("Not supported: ${this::class}") {
@@ -423,7 +423,7 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
return emptyList()
}
override fun createErrorType(debugName: String): SimpleTypeMarker {
override fun createErrorType(debugName: String, delegatedType: SimpleTypeMarker?): SimpleTypeMarker {
TODO("IrTypeSystemContext doesn't support constraint system resolution")
}
@@ -82,7 +82,7 @@ object NewCommonSuperTypeCalculator {
stateStubTypesNotEqual: TypeCheckerState
): SimpleTypeMarker {
if (types.any { it.isError() }) {
return createErrorType("CST(${types.joinToString()}")
return createErrorType("CST(${types.joinToString()}", delegatedType = null)
}
// i.e. result type also should be marked nullable
@@ -456,7 +456,10 @@ abstract class AbstractTypeApproximator(
val typeConstructor = type.typeConstructor()
if (typeConstructor.parametersCount() != type.argumentsCount()) {
return if (conf.errorType) {
createErrorType("Inconsistent type: $type (parameters.size = ${typeConstructor.parametersCount()}, arguments.size = ${type.argumentsCount()})")
createErrorType(
"Inconsistent type: $type (parameters.size = ${typeConstructor.parametersCount()}, arguments.size = ${type.argumentsCount()})",
type
)
} else type.defaultResult(toSuper)
}
@@ -490,7 +493,8 @@ abstract class AbstractTypeApproximator(
return if (conf.errorType) {
createErrorType(
"Inconsistent type: $type ($index parameter has declared variance: ${parameter.getVariance()}, " +
"but argument variance is ${argument.getVariance()})"
"but argument variance is ${argument.getVariance()})",
type
)
} else type.defaultResult(toSuper)
}
@@ -0,0 +1,10 @@
// -- Module: <m1> --
// -- Module: <m2> --
// -- Module: <m3> --
// -- Module: <m4> --
/Call.kt:90:19: error: unresolved reference: result
fun simple(b: com.result.B<*>) {}
^
@@ -0,0 +1,7 @@
/Call.kt:(253,262): error: Cannot access class 'com.result.B'. Check your module classpath for missing or conflicting dependencies.
/Call.kt:(294,296): error: Cannot access class 'com.result.B'. Check your module classpath for missing or conflicting dependencies.
/Call.kt:(346,355): error: Cannot access class 'com.result.Owner.Nested.VeryNested'. Check your module classpath for missing or conflicting dependencies.
/Call.kt:(383,389): error: Unresolved reference 'result'.
+90
View File
@@ -0,0 +1,90 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// RENDER_DIAGNOSTICS_FULL_TEXT
// ISSUE: KT-62525
// MODULE: m1
// FILE: Request.kt
package com.request
sealed class Result<out Success, out Error> {
class Success<out Success>(val value: Success) : Result<Success, Nothing>()
class Error<out Error>(val error: Error) : Result<Nothing, Error>()
inline fun <Mapped> mapError(transform: (Error) -> Mapped): Result<Success, Mapped> =
when (this) {
is Result.Success -> this
is Result.Error -> Error(transform(error))
}
}
fun <T, U> request(success: T, error: U): Result<T, U> {
if (1 + 1 + 2 == 4) {
return Result.Success(success)
} else {
return Result.Error(error)
}
}
// MODULE: m2
// FILE: Result.kt
package com.result
class A {}
sealed class B<T> {
class HttpError<T>(val response: T) : B<T>() {}
class Exception<T>(val exception: Throwable) : B<T>() {}
}
class C {}
class Owner<T> {
inner class Nested<S> {
inner class VeryNested<R, P>
}
}
// MODULE: m3(m1, m2)
// FILE: Repo.kt
package com.repo
import com.request.Result
import com.request.request
import com.result.A
import com.result.B
import com.result.C
import com.result.Owner
import java.lang.RuntimeException
fun request_a(): Result<A, B<C>> {
return request<A, B<C>>(A(), B.Exception(RuntimeException("Error")))
}
fun request_withNested(): Result<Owner<C>.Nested<A>, Owner<A>.Nested<C>.VeryNested<C, A>> {
return request<Owner<C>.Nested<A>, Owner<A>.Nested<C>.VeryNested<C, A>>(Owner<C>().Nested<A>(), Owner<A>().Nested<C>().VeryNested<C, A>())
}
// MODULE: m4(m3, m1)
// FILE: Call.kt
package com.call
import com.repo.request_a
import com.repo.request_withNested
class Model {
fun call() {
request_a().mapError <!MISSING_DEPENDENCY_CLASS!>{ 1 + 1 }<!>
request_a().mapError { <!MISSING_DEPENDENCY_CLASS!>it<!> -> 1 + 1 }
request_withNested().mapError <!MISSING_DEPENDENCY_CLASS!>{ 1 + 1 }<!>
}
}
fun simple(b: com.<!UNRESOLVED_REFERENCE!>result<!>.B<*>) {}
+90
View File
@@ -0,0 +1,90 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// RENDER_DIAGNOSTICS_FULL_TEXT
// ISSUE: KT-62525
// MODULE: m1
// FILE: Request.kt
package com.request
sealed class Result<out Success, out Error> {
class Success<out Success>(val value: Success) : Result<Success, Nothing>()
class Error<out Error>(val error: Error) : Result<Nothing, Error>()
inline fun <Mapped> mapError(transform: (Error) -> Mapped): Result<Success, Mapped> =
when (this) {
is Result.Success -> <!DEBUG_INFO_SMARTCAST!>this<!>
is Result.Error -> Error(transform(<!DEBUG_INFO_IMPLICIT_RECEIVER_SMARTCAST!>error<!>))
}
}
fun <T, U> request(success: T, error: U): Result<T, U> {
if (1 + 1 + 2 == 4) {
return Result.Success(success)
} else {
return Result.Error(error)
}
}
// MODULE: m2
// FILE: Result.kt
package com.result
class A {}
sealed class B<T> {
class HttpError<T>(val response: T) : B<T>() {}
class Exception<T>(val exception: Throwable) : B<T>() {}
}
class C {}
class Owner<T> {
inner class Nested<S> {
inner class VeryNested<R, P>
}
}
// MODULE: m3(m1, m2)
// FILE: Repo.kt
package com.repo
import com.request.Result
import com.request.request
import com.result.A
import com.result.B
import com.result.C
import com.result.Owner
import java.lang.RuntimeException
fun request_a(): Result<A, B<C>> {
return request<A, B<C>>(A(), B.Exception(RuntimeException("Error")))
}
fun request_withNested(): Result<Owner<C>.Nested<A>, Owner<A>.Nested<C>.VeryNested<C, A>> {
return request<Owner<C>.Nested<A>, Owner<A>.Nested<C>.VeryNested<C, A>>(Owner<C>().Nested<A>(), Owner<A>().Nested<C>().VeryNested<C, A>())
}
// MODULE: m4(m3, m1)
// FILE: Call.kt
package com.call
import com.repo.request_a
import com.repo.request_withNested
class Model {
fun call() {
request_a().mapError { 1 + 1 }
request_a().mapError { it -> 1 + 1 }
request_withNested().mapError { 1 + 1 }
}
}
fun simple(b: com.<!UNRESOLVED_REFERENCE!>result<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>B<!><*>) {}
@@ -22742,6 +22742,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/javac/Annotations.kt");
}
@Test
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/Lambda.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -30,6 +30,12 @@ public class DiagnosticUsingJavacTestGenerated extends AbstractDiagnosticUsingJa
runTest("compiler/testData/diagnostics/tests/javac/Annotations.kt");
}
@Test
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/Lambda.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -87,7 +87,7 @@ interface TypeSystemTypeFactoryContext: TypeSystemBuiltInsContext {
fun createTypeArgument(type: KotlinTypeMarker, variance: TypeVariance): TypeArgumentMarker
fun createStarProjection(typeParameter: TypeParameterMarker): TypeArgumentMarker
fun createErrorType(debugName: String): SimpleTypeMarker
fun createErrorType(debugName: String, delegatedType: SimpleTypeMarker?): SimpleTypeMarker
fun createUninferredType(constructor: TypeConstructorMarker): KotlinTypeMarker
}
@@ -597,7 +597,6 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
override fun SimpleTypeMarker.replaceArguments(replacement: (TypeArgumentMarker) -> TypeArgumentMarker): SimpleTypeMarker {
require(this is SimpleType, this::errorMessage)
@Suppress("UNCHECKED_CAST")
return this.replaceArgumentsByExistingArgumentsWith(replacement)
}
@@ -714,7 +713,7 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
return captureFromExpressionInternal(type as UnwrappedType)
}
override fun createErrorType(debugName: String): SimpleTypeMarker {
override fun createErrorType(debugName: String, delegatedType: SimpleTypeMarker?): SimpleTypeMarker {
return ErrorUtils.createErrorType(ErrorTypeKind.RESOLUTION_ERROR_TYPE, debugName)
}
@@ -922,8 +921,6 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
override val isK2: Boolean
get() = false
class WA // Workaround for KT-52313
}
fun TypeVariance.convertVariance(): Variance {