K2: report only warning on lambda parameters with missing dependencies

During the fix of KT-62525, we've forbidden to use lambda parameters
with inaccessible types at all. After it, some impact was noticed,
so we decided to forbid them only in case it's necessary
(the case when associated types have type arguments, see KT-62525
description), and to deprecate them in other cases.

#KT-64266 Fixed
This commit is contained in:
Mikhail Glukhikh
2023-12-19 14:51:55 +01:00
committed by Space Team
parent 44aa2d86d3
commit c322644860
22 changed files with 172 additions and 27 deletions
@@ -431,6 +431,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER) { firDiagnostic ->
MissingDependencyClassInLambdaParameterImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS) { firDiagnostic ->
CreatingAnInstanceOfAbstractClassImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -348,6 +348,11 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val declarationType: KtType
}
interface MissingDependencyClassInLambdaParameter : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = MissingDependencyClassInLambdaParameter::class
val type: KtType
}
interface CreatingAnInstanceOfAbstractClass : KtFirDiagnostic<KtExpression> {
override val diagnosticClass get() = CreatingAnInstanceOfAbstractClass::class
}
@@ -401,6 +401,12 @@ internal class MissingDependencySuperclassImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.MissingDependencySuperclass
internal class MissingDependencyClassInLambdaParameterImpl(
override val type: KtType,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.MissingDependencyClassInLambdaParameter
internal class CreatingAnInstanceOfAbstractClassImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
@@ -22892,6 +22892,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGeneric.kt");
}
@Test
@TestMetadata("LambdaNonGenericForbidden.kt")
public void testLambdaNonGenericForbidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGenericForbidden.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -22892,6 +22892,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGeneric.kt");
}
@Test
@TestMetadata("LambdaNonGenericForbidden.kt")
public void testLambdaNonGenericForbidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGenericForbidden.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -22886,6 +22886,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGeneric.kt");
}
@Test
@TestMetadata("LambdaNonGenericForbidden.kt")
public void testLambdaNonGenericForbidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGenericForbidden.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -22892,6 +22892,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGeneric.kt");
}
@Test
@TestMetadata("LambdaNonGenericForbidden.kt")
public void testLambdaNonGenericForbidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGenericForbidden.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -183,6 +183,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<ConeKotlinType>("missingType")
parameter<ConeKotlinType>("declarationType")
}
val MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER by warning<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<ConeKotlinType>("type")
}
}
val CALL_RESOLUTION by object : DiagnosticGroup("Call resolution") {
@@ -186,6 +186,7 @@ object FirErrors {
val DUPLICATE_PARAMETER_NAME_IN_FUNCTION_TYPE: KtDiagnosticFactory0 by error0<KtTypeReference>()
val MISSING_DEPENDENCY_CLASS: KtDiagnosticFactory1<ConeKotlinType> by error1<PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val MISSING_DEPENDENCY_SUPERCLASS: KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> by error2<PsiElement, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER: KtDiagnosticFactory1<ConeKotlinType> by warning1<PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
// Call resolution
val CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS: KtDiagnosticFactory0 by error0<KtExpression>()
@@ -9,6 +9,7 @@ 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.symbols.impl.FirAnonymousFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
@@ -18,8 +19,11 @@ object FirMissingDependencyClassForParameterChecker : FirValueParameterChecker()
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val containingFunctionSymbol = declaration.containingFunctionSymbol
if (containingFunctionSymbol !is FirAnonymousFunctionSymbol || !containingFunctionSymbol.isLambda) return
val missingTypes = mutableSetOf<ConeKotlinType>()
considerType(declaration.returnTypeRef.coneType, missingTypes, context)
reportMissingTypes(declaration.source, missingTypes, context, reporter)
reportMissingTypes(declaration.source, missingTypes, context, reporter, isTypeOfLambdaParameter = true)
}
}
@@ -6,11 +6,13 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.references.isError
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithSingleCandidate
@@ -39,7 +41,7 @@ object FirMissingDependencyClassChecker : FirQualifiedAccessExpressionChecker(),
(symbol as? FirFunctionSymbol<*>)?.valueParameterSymbols?.forEach {
considerType(it.resolvedReturnTypeRef.coneType, missingTypes, context)
}
reportMissingTypes(expression.source, missingTypes, context, reporter)
reportMissingTypes(expression.source, missingTypes, context, reporter, isTypeOfLambdaParameter = false)
}
}
@@ -76,7 +78,7 @@ internal interface FirMissingDependencyClassProxy {
}
if (hasMissingClass && !hasError) {
val reportedType = type.withNullability(ConeNullability.NOT_NULL, context.session.typeContext).withArguments(emptyArray())
val reportedType = type.withNullability(ConeNullability.NOT_NULL, context.session.typeContext)
missingTypes.add(reportedType)
}
}
@@ -86,9 +88,20 @@ internal interface FirMissingDependencyClassProxy {
missingTypes: MutableSet<ConeKotlinType>,
context: CheckerContext,
reporter: DiagnosticReporter,
isTypeOfLambdaParameter: Boolean,
) {
val reported = mutableSetOf<ConeKotlinType>()
for (missingType in missingTypes) {
reporter.reportOn(source, FirErrors.MISSING_DEPENDENCY_CLASS, missingType, context)
val withoutArguments = missingType.withArguments(emptyArray())
if (withoutArguments in reported) continue
if (isTypeOfLambdaParameter && missingType.typeArguments.isEmpty() &&
!context.session.languageVersionSettings.supportsFeature(LanguageFeature.ForbidLambdaParameterWithMissingDependencyType)
) {
reporter.reportOn(source, FirErrors.MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER, withoutArguments, context)
} else {
reporter.reportOn(source, FirErrors.MISSING_DEPENDENCY_CLASS, withoutArguments, context)
reported += withoutArguments
}
}
}
}
@@ -381,6 +381,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_CONSTRUCTOR_KEYWORD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_DEPENDENCY_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_DEPENDENCY_SUPERCLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_STDLIB_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER
@@ -788,6 +789,11 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
RENDER_TYPE,
RENDER_TYPE,
)
map.put(
MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER,
"Class ''{0}'' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.",
RENDER_TYPE,
)
map.put(ASSIGNMENT_IN_EXPRESSION_CONTEXT, "Only expressions are allowed in this context.")
map.put(EXPRESSION_EXPECTED, "Only expressions are allowed here.")
@@ -102,6 +102,7 @@ sealed class FirFunctionWithoutNameSymbol<F : FirFunction>(stubName: Name) : Fir
class FirAnonymousFunctionSymbol : FirFunctionWithoutNameSymbol<FirAnonymousFunction>(Name.identifier("anonymous")) {
val label: FirLabel? get() = fir.label
val isLambda: Boolean get() = fir.isLambda
}
open class FirPropertyAccessorSymbol : FirFunctionWithoutNameSymbol<FirPropertyAccessor>(Name.identifier("accessor")) {
@@ -1,15 +1,15 @@
// -- Module: <m1> --
// -- Module: <m2> --
/m2.kt:13:9: warning: parameter 'f' is never used
/m2.kt:14:9: warning: parameter 'f' is never used
fun foo(f: (Some, String) -> Unit) {}
^
/m2.kt:14:9: warning: parameter 'f' is never used
/m2.kt:15:9: warning: parameter 'f' is never used
fun bar(f: (Some) -> Unit) {}
^
// -- Module: <m3> --
/m3.kt:23:17: error: unresolved reference: Some
/m3.kt:24:17: error: unresolved reference: Some
foo { some: Some, _ -> }
^
@@ -1,23 +1,23 @@
/m3.kt:(72,73): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(73,74): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(92,96): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(93,97): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(117,121): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(118,122): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(128,132): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(129,133): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(162,166): error: Unresolved reference 'Some'.
/m3.kt:(163,167): error: Unresolved reference 'Some'.
/m3.kt:(184,187): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(185,188): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(198,199): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(199,200): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(215,217): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(216,218): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(231,248): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(232,249): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(233,235): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(234,236): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(259,263): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(260,264): warning: Class 'Some' of the lambda parameter is inaccessible. While it may work, this case is dangerous and may be forbidden soon. Consider adding a necessary dependency to your module classpath.
/m3.kt:(267,271): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
/m3.kt:(268,272): error: Cannot access class 'Some'. Check your module classpath for missing or conflicting dependencies.
@@ -1,5 +1,6 @@
// RENDER_DIAGNOSTICS_FULL_TEXT
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER
// LANGUAGE: -ForbidLambdaParameterWithMissingDependencyType
// ISSUE: KT-64266
// MODULE: m1
@@ -17,14 +18,14 @@ fun bar(f: (Some) -> Unit) {}
// FILE: m3.kt
fun test() {
foo { <!MISSING_DEPENDENCY_CLASS!>_<!>, _ -> }
foo { <!MISSING_DEPENDENCY_CLASS!>some<!>, str -> }
foo { <!MISSING_DEPENDENCY_CLASS!>some<!>, _ -> <!MISSING_DEPENDENCY_CLASS!>some<!>.toString() }
foo { <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>_<!>, _ -> }
foo { <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>some<!>, str -> }
foo { <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>some<!>, _ -> <!MISSING_DEPENDENCY_CLASS!>some<!>.toString() }
foo { some: <!UNRESOLVED_REFERENCE!>Some<!>, _ -> }
bar <!MISSING_DEPENDENCY_CLASS!>{ }<!>
bar { <!MISSING_DEPENDENCY_CLASS!>_<!> -> }
bar { <!MISSING_DEPENDENCY_CLASS!>it<!> -> }
bar <!MISSING_DEPENDENCY_CLASS!>{ <!MISSING_DEPENDENCY_CLASS!>it<!>.toString() }<!>
bar { <!MISSING_DEPENDENCY_CLASS!>some<!> -> <!MISSING_DEPENDENCY_CLASS!>some<!>.toString() }
bar <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>{ }<!>
bar { <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>_<!> -> }
bar { <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>it<!> -> }
bar <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>{ <!MISSING_DEPENDENCY_CLASS!>it<!>.toString() }<!>
bar { <!MISSING_DEPENDENCY_CLASS_IN_LAMBDA_PARAMETER!>some<!> -> <!MISSING_DEPENDENCY_CLASS!>some<!>.toString() }
}
@@ -1,5 +1,6 @@
// RENDER_DIAGNOSTICS_FULL_TEXT
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER
// LANGUAGE: -ForbidLambdaParameterWithMissingDependencyType
// ISSUE: KT-64266
// MODULE: m1
@@ -0,0 +1,30 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER
// LANGUAGE: +ForbidLambdaParameterWithMissingDependencyType
// ISSUE: KT-64266
// MODULE: m1
// FILE: m1.kt
class Some
// MODULE: m2(m1)
// FILE: m2.kt
fun foo(f: (Some, String) -> Unit) {}
fun bar(f: (Some) -> Unit) {}
// MODULE: m3(m2)
// FILE: m3.kt
fun test() {
foo { <!MISSING_DEPENDENCY_CLASS!>_<!>, _ -> }
foo { <!MISSING_DEPENDENCY_CLASS!>some<!>, str -> }
foo { <!MISSING_DEPENDENCY_CLASS!>some<!>, _ -> <!MISSING_DEPENDENCY_CLASS!>some<!>.toString() }
foo { some: <!UNRESOLVED_REFERENCE!>Some<!>, _ -> }
bar <!MISSING_DEPENDENCY_CLASS!>{ }<!>
bar { <!MISSING_DEPENDENCY_CLASS!>_<!> -> }
bar { <!MISSING_DEPENDENCY_CLASS!>it<!> -> }
bar <!MISSING_DEPENDENCY_CLASS!>{ <!MISSING_DEPENDENCY_CLASS!>it<!>.toString() }<!>
bar { <!MISSING_DEPENDENCY_CLASS!>some<!> -> <!MISSING_DEPENDENCY_CLASS!>some<!>.toString() }
}
@@ -0,0 +1,30 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER
// LANGUAGE: +ForbidLambdaParameterWithMissingDependencyType
// ISSUE: KT-64266
// MODULE: m1
// FILE: m1.kt
class Some
// MODULE: m2(m1)
// FILE: m2.kt
fun foo(f: (Some, String) -> Unit) {}
fun bar(f: (Some) -> Unit) {}
// MODULE: m3(m2)
// FILE: m3.kt
fun test() {
foo { _, _ -> }
foo { some, str -> }
foo { some, _ -> some.toString() }
foo { some: <!UNRESOLVED_REFERENCE!>Some<!>, _ -> }
bar { }
bar { _ -> }
bar { it -> }
bar { it.toString() }
bar { some -> some.toString() }
}
@@ -22892,6 +22892,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGeneric.kt");
}
@Test
@TestMetadata("LambdaNonGenericForbidden.kt")
public void testLambdaNonGenericForbidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGenericForbidden.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -42,6 +42,12 @@ public class DiagnosticUsingJavacTestGenerated extends AbstractDiagnosticUsingJa
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGeneric.kt");
}
@Test
@TestMetadata("LambdaNonGenericForbidden.kt")
public void testLambdaNonGenericForbidden() throws Exception {
runTest("compiler/testData/diagnostics/tests/javac/LambdaNonGenericForbidden.kt");
}
@Nested
@TestMetadata("compiler/testData/diagnostics/tests/javac/fieldsResolution")
@TestDataPath("$PROJECT_ROOT")
@@ -316,6 +316,7 @@ enum class LanguageFeature(
PrioritizedEnumEntries(KOTLIN_2_1, kind = UNSTABLE_FEATURE), // KT-58920
ProhibitInlineModifierOnPrimaryConstructorParameters(KOTLIN_2_1, kind = BUG_FIX), // KT-59664
ProhibitSingleNamedFunctionAsExpression(KOTLIN_2_1, kind = BUG_FIX), // KT-62573
ForbidLambdaParameterWithMissingDependencyType(KOTLIN_2_1, kind = BUG_FIX), // KT-64266
// End of 2.* language features --------------------------------------------------