diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 779a1d4c009..96de632134f 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3580,6 +3580,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SAFE_CALLABLE_REFERENCE_CALL) { firDiagnostic -> + SafeCallableReferenceCallImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL) { firDiagnostic -> LateinitIntrinsicCallOnNonLiteralImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 014a6e03c3e..51d83666a02 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2517,6 +2517,10 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { val kind: String } + interface SafeCallableReferenceCall : KtFirDiagnostic { + override val diagnosticClass get() = SafeCallableReferenceCall::class + } + interface LateinitIntrinsicCallOnNonLiteral : KtFirDiagnostic { override val diagnosticClass get() = LateinitIntrinsicCallOnNonLiteral::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index d8f5221d568..9a147a045b7 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3029,6 +3029,11 @@ internal class ExplicitTypeArgumentsInPropertyAccessImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.ExplicitTypeArgumentsInPropertyAccess +internal class SafeCallableReferenceCallImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.SafeCallableReferenceCall + internal class LateinitIntrinsicCallOnNonLiteralImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 1fffa1e7187..c0457ea7a93 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1232,6 +1232,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) { parameter("kind") } + val SAFE_CALLABLE_REFERENCE_CALL by error() val LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL by error() val LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT by error() diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index f4085341987..4b8ce604215 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -646,6 +646,7 @@ object FirErrors { val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING: KtDiagnosticFactory0 by warning0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS: KtDiagnosticFactory0 by error0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS: KtDiagnosticFactory1 by error1(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + val SAFE_CALLABLE_REFERENCE_CALL: KtDiagnosticFactory0 by error0() val LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL: KtDiagnosticFactory0 by error0() val LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT: KtDiagnosticFactory0 by error0() val LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION: KtDiagnosticFactory0 by error0() diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt index 5959330a90e..94dc9a9a3c9 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt @@ -432,6 +432,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set = setOf( "ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS", "LOCAL_VARIABLE_WITH_TYPE_PARAMETERS", "EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS", + "SAFE_CALLABLE_REFERENCE_CALL", "LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL", "LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT", "LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION", diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt index 4d83a0fcbbb..1bf23de1aea 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.references.resolved import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol @@ -28,6 +29,10 @@ object FirCallableReferenceChecker : FirQualifiedAccessExpressionChecker() { override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { if (expression !is FirCallableReferenceAccess) return + if (expression.hasQuestionMarkAtLHS && expression.explicitReceiver !is FirResolvedQualifier) { + reporter.reportOn(expression.source, FirErrors.SAFE_CALLABLE_REFERENCE_CALL, context) + } + // UNRESOLVED_REFERENCE will be reported separately. val reference = expression.calleeReference.resolved ?: return val referredSymbol = reference.resolvedSymbol diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 146dba950c8..65983fdcd47 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -569,6 +569,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH_BY_DELEGATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH_ON_INHERITANCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH_ON_OVERRIDE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SAFE_CALLABLE_REFERENCE_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SAFE_CALL_WILL_CHANGE_NULLABILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_CLASS_CONSTRUCTOR_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_INHERITOR_IN_DIFFERENT_MODULE @@ -1980,6 +1981,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING, "Type parameters for local variables are deprecated.") map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS, "Local variables cannot have type parameters.") map.put(EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS, "{0} access cannot have explicit type arguments.", STRING) + map.put(SAFE_CALLABLE_REFERENCE_CALL, "This syntax is reserved for future releases.") map.put(LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL, "This declaration can only be called on a property literal (e.g. 'Foo::bar').") map.put(LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT, "This declaration can only be called on a reference to a 'lateinit' property.") diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt index 0bbe805ba4d..5da7d0cd1bb 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt @@ -19,9 +19,9 @@ class Test { fun List.testCallable1(): () -> Unit = a::foo fun List.testCallable1a(): () -> Unit = a::foo - fun List.testCallable2(): () -> Unit = b?::foo + fun List.testCallable2(): () -> Unit = b?::foo fun List.testCallable3(): () -> Unit = b::foo - fun List.testCallable4(): () -> Unit = b?::foo + fun List.testCallable4(): () -> Unit = b?::foo fun List.testClassLiteral1() = a::class fun List.testClassLiteral1a() = a::class @@ -31,5 +31,5 @@ class Test { fun List.testUnresolved1() = unresolved::foo fun List.testUnresolved2() = a<unresolved>::foo fun List.testUnresolved3() = a<>::foo - fun List.testUnresolved4() = unresolved?::foo + fun List.testUnresolved4() = unresolved?::foo } diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.fir.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.fir.kt index 254f337237f..bd2421e0ffd 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax2.fir.kt @@ -4,4 +4,4 @@ package test fun nullableFun(): Int? = null fun Int.foo() {} -val test1 = nullableFun()?::foo +val test1 = nullableFun()?::foo diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt index 42cc0877a3d..45d338becee 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt @@ -24,6 +24,6 @@ class Test { val test1: () -> Right = a.b.c::foo val test1a: () -> Right = a.b.c::foo - val test2: () -> Right = a.b.c?::foo - val test2a: () -> Right = a.b.c?::foo + val test2: () -> Right = a.b.c?::foo + val test2a: () -> Right = a.b.c?::foo } diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax4.fir.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax4.fir.kt index 5b64d457640..89c8228afb3 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax4.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax4.fir.kt @@ -55,7 +55,7 @@ fun rain() { a.b.c::foo a.b.c?::class - a.b.c?::foo + a.b.c?::foo a.b?.c::class a.b?.c::foo @@ -70,5 +70,5 @@ fun rain() { a.b.maybeC::foo a.b.maybeC?::class - a.b.maybeC?::foo + a.b.maybeC?::foo } diff --git a/compiler/testData/diagnostics/tests/callableReference/withQuestionMarks.fir.kt b/compiler/testData/diagnostics/tests/callableReference/withQuestionMarks.fir.kt index 597dac3e342..0723e4f5950 100644 --- a/compiler/testData/diagnostics/tests/callableReference/withQuestionMarks.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/withQuestionMarks.fir.kt @@ -4,24 +4,24 @@ fun foo() {} fun main() { - val x1 = logger::info?::print - val x2 = logger?::info?::print - val x3 = logger?::info::print - val x4 = logger?::info?::print?::print?::print?::print?::print?::print?::print?::print?::print?::print - val x5 = logger::info?::print?::print?::print?::print?::print?::print?::print?::print?::print?::print - val x6 = logger!!::info?::print?::print - val x7 = logger::info!!::print?::print - val x8 = logger?::info!!::print?::print - val x9 = logger!!::info?::print?::print - val x10 = logger::info?::print!!::print + val x1 = logger::info?::print + val x2 = logger?::info?::print + val x3 = logger?::info::print + val x4 = logger?::info?::print?::print?::print?::print?::print?::print?::print?::print?::print?::print + val x5 = logger::info?::print?::print?::print?::print?::print?::print?::print?::print?::print?::print + val x6 = logger!!::info?::print?::print + val x7 = logger::info!!::print?::print + val x8 = logger?::info!!::print?::print + val x9 = logger!!::info?::print?::print + val x10 = logger::info?::print!!::print val x11 = logger!!::info!!::print!!::print - val x12 = logger?::info!!::print!!::print - val x13 = 42?::unresolved?::print + val x12 = logger?::info!!::print!!::print + val x13 = 42?::unresolved?::print val x14 = logger?!!::info?::print?::print val x15 = logger::info?!!::print?::print - val x16 = logger!!?::info?::print?::print - val x17 = logger::info!!?::print?::print + val x16 = logger!!?::info?::print?::print + val x17 = logger::info!!?::print?::print // It must be OK val x18 = String?::hashCode ?: ::foo