From d4e6a4ad54f5f4ca6c0771d180db669124280c40 Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Wed, 23 Nov 2022 16:04:07 +0100 Subject: [PATCH] [FIR] KT-54978 Prohibit explicit type arguments in property accesses - Add a checker which ensures that property accesses have no explicit type arguments. If an error on the property access's callee reference already exists, the new error is not reported in favor of the existing error, as the property access may have been intended to be a function call. - `complicatedLTGT.fir.kt`: The underlying parser issue is not yet solved, which is why `x` is parsed as a property access with explicit type arguments. - `reservedExpressionSyntax` tests: This new check makes a lot of the access expressions in these tests illegal, so valid lines have been added and invalid lines appropriately marked with `EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS` errors. ^KT-54978 fixed --- .../diagnostics/KtFirDataClassConverters.kt | 6 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 5 ++ ...nosisCompilerFirTestdataTestGenerated.java | 6 ++ ...TouchedTilContractsPhaseTestGenerated.java | 5 ++ ...rtyAccessWithExplicitTypeArguments.fir.txt | 88 +++++++++++++++++++ ...propertyAccessWithExplicitTypeArguments.kt | 85 ++++++++++++++++++ .../runners/FirDiagnosticTestGenerated.java | 6 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 ++ .../kotlin/fir/checkers/generator/Main.kt | 1 + .../diagnostics/FirDiagnosticsList.kt | 1 + .../expression/ComposedExpressionCheckers.kt | 4 + .../checkers/expression/ExpressionCheckers.kt | 2 + .../expression/FirExpressionCheckerAliases.kt | 2 + .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/CommonExpressionCheckers.kt | 5 ++ .../FirPropertyAccessTypeArgumentsChecker.kt | 27 ++++++ .../ExpressionCheckersDiagnosticComponent.kt | 2 +- .../diagnostics/FirErrorsDefaultMessages.kt | 2 + .../bound/reservedExpressionSyntax.fir.kt | 14 +-- .../bound/reservedExpressionSyntax.kt | 6 +- .../bound/reservedExpressionSyntax.txt | 2 + .../bound/reservedExpressionSyntax3.fir.kt | 7 +- .../bound/reservedExpressionSyntax3.kt | 7 +- .../bound/reservedExpressionSyntax3.txt | 1 + .../tests/syntax/complicatedLTGT.fir.kt | 2 +- 26 files changed, 281 insertions(+), 16 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirPropertyAccessTypeArgumentsChecker.kt 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 26fe17949c8..a94a4e9f644 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 @@ -2844,6 +2844,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS) { firDiagnostic -> + ExplicitTypeArgumentsInPropertyAccessImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.EXPECTED_DECLARATION_WITH_BODY) { firDiagnostic -> ExpectedDeclarationWithBodyImpl( 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 d991121749c..07328327fa9 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 @@ -2008,6 +2008,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = LocalVariableWithTypeParameters::class } + abstract class ExplicitTypeArgumentsInPropertyAccess : KtFirDiagnostic() { + override val diagnosticClass get() = ExplicitTypeArgumentsInPropertyAccess::class + } + abstract class ExpectedDeclarationWithBody : KtFirDiagnostic() { override val diagnosticClass get() = ExpectedDeclarationWithBody::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 26a594c2a6d..44ba3108c4a 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 @@ -2418,6 +2418,11 @@ internal class LocalVariableWithTypeParametersImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.LocalVariableWithTypeParameters(), KtAbstractFirDiagnostic +internal class ExplicitTypeArgumentsInPropertyAccessImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.ExplicitTypeArgumentsInPropertyAccess(), KtAbstractFirDiagnostic + internal class ExpectedDeclarationWithBodyImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index 681fa38430e..266a34adcd8 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -2159,6 +2159,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/privateVisibility.kt"); } + @Test + @TestMetadata("propertyAccessWithExplicitTypeArguments.kt") + public void testPropertyAccessWithExplicitTypeArguments() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt"); + } + @Test @TestMetadata("protectedVisibility.kt") public void testProtectedVisibility() throws Exception { diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index e8bc116e3f5..3c9c1da0b11 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -1881,6 +1881,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/privateVisibility.kt"); } + @TestMetadata("propertyAccessWithExplicitTypeArguments.kt") + public void testPropertyAccessWithExplicitTypeArguments() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt"); + } + @TestMetadata("protectedVisibility.kt") public void testProtectedVisibility() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/protectedVisibility.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.fir.txt b/compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.fir.txt new file mode 100644 index 00000000000..83e4d64aea4 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.fir.txt @@ -0,0 +1,88 @@ +FILE: propertyAccessWithExplicitTypeArguments.kt + public final fun f1(x: R|kotlin/Int|): R|kotlin/Unit| { + lval y: R|kotlin/Int| = Int(5) + lval s: R|kotlin/String| = String(hello) + R|/x| + R|/x| + R|/y| + R|/y| + R|/s| + R|/s| + } + public final val property: R|kotlin/Int| = Int(10) + public get(): R|kotlin/Int| + public final fun f2(): R|kotlin/Unit| { + R|/property| + } + public final val property2: R|kotlin/Int| + public get(): R|kotlin/Int| { + ^ Int(10) + } + public final fun f3(): R|kotlin/Unit| { + R|/property2| + } + public abstract interface Context : R|kotlin/Any| { + } + public final class ContextImpl : R|Context| { + public constructor(): R|ContextImpl| { + super() + } + + } + public final class Receiver : R|kotlin/Any| { + public constructor(): R|Receiver| { + super() + } + + } + public final val R|Receiver|.hello1: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String(hello 1) + } + context(R|Context|) + public final val hello2: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String(hello 2) + } + context(R|Context|) + public final val R|Receiver|.hello3: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String(hello 3) + } + public final operator fun R|kotlin/String|.invoke(): R|kotlin/String| { + ^invoke String(world) + } + public final fun f4(): R|kotlin/Unit| { + lval receiver: R|Receiver| = R|/Receiver.Receiver|() + R|/receiver|.R|/hello1| + R|/receiver|.R|/hello1| + R|/receiver|.R|/hello1|.R|/invoke|() + R|/receiver|.R|/hello1| + R|/receiver|.R|/hello1| + R|/receiver|.R|/hello1| + R|kotlin/with||, R|kotlin/String|>(R|/ContextImpl.ContextImpl|(), = with@fun R|ContextImpl|.(): R|kotlin/String| { + R|/hello2| + R|/hello2| + R|/hello2|.R|/invoke|() + R|/hello2| + R|/hello2| + R|/hello2| + R|/receiver|.R|/hello3| + R|/receiver|.R|/hello3| + R|/receiver|.R|/hello3|.R|/invoke|() + R|/receiver|.R|/hello3| + R|/receiver|.R|/hello3| + ^ R|/receiver|.R|/hello3| + } + ) + } + public final val R|Receiver|.helloReified: R|kotlin/String| + public inline get(): R|kotlin/String| { + ^ String(hello) + } + public final fun f5(): R|kotlin/Unit| { + lval receiver: R|Receiver| = R|/Receiver.Receiver|() + R|/receiver|.R|/helloReified| + R|/receiver|.R|/helloReified| + R|/receiver|.R|/helloReified| + } diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt b/compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt new file mode 100644 index 00000000000..7a5f19714e2 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt @@ -0,0 +1,85 @@ +// ISSUE: KT-54978 + +// Case 1: Parameters and local variables +fun f1(x: Int) { + val y = 5 + val s = "hello" + + x + x + y + y + s + s +} + +// Case 2: Simple property +val property: Int = 10 + +fun f2() { + property +} + +// Case 3: Simple property with getter +val property2: Int + get() = 10 + +fun f3() { + property2 +} + +// Case 4: Property with extension and/or context receiver +interface Context +class ContextImpl : Context +class Receiver + +val Receiver.hello1: String + get() = "hello 1" + +context(Context) +val hello2: String + get() = "hello 2" + +context(Context) +val Receiver.hello3: String + get() = "hello 3" + +operator fun String.invoke(): String = "world" + +fun f4() { + val receiver = Receiver() + + receiver.hello1 + receiver.hello1 + receiver.hello1() // legal `String.invoke` call + receiver.hello1 + receiver.hello1 + receiver.hello1 + + with (ContextImpl()) { + hello2 + hello2 + hello2() // legal `String.invoke` call + hello2 + hello2 + hello2 + + receiver.hello3 + receiver.hello3 + receiver.hello3() // legal `String.invoke` call + receiver.hello3 + receiver.hello3 + receiver.hello3 + } +} + +// Case 5: Property with receiver and reified type parameter +inline val Receiver.helloReified: String + get() = "hello" + +fun f5() { + val receiver = Receiver() + receiver.helloReified + receiver.helloReified + receiver.helloReified +} diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index 663b7882872..2dce587400e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -2159,6 +2159,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/privateVisibility.kt"); } + @Test + @TestMetadata("propertyAccessWithExplicitTypeArguments.kt") + public void testPropertyAccessWithExplicitTypeArguments() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt"); + } + @Test @TestMetadata("protectedVisibility.kt") public void testProtectedVisibility() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 89a3898872f..d100318a864 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -2159,6 +2159,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/privateVisibility.kt"); } + @Test + @TestMetadata("propertyAccessWithExplicitTypeArguments.kt") + public void testPropertyAccessWithExplicitTypeArguments() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/expresssions/propertyAccessWithExplicitTypeArguments.kt"); + } + @Test @TestMetadata("protectedVisibility.kt") public void testProtectedVisibility() throws Exception { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt index 59523c8e015..4d4e236226d 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/Main.kt @@ -35,6 +35,7 @@ fun main(args: Array) { alias("QualifiedAccessExpressionChecker") alias("CallChecker") alias("FunctionCallChecker") + alias("PropertyAccessExpressionChecker") alias("IntegerLiteralOperatorCallChecker") alias("VariableAssignmentChecker") alias("TryExpressionChecker") 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 ad0e93a786d..56ba9175a58 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 @@ -1007,6 +1007,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS by error(PositioningStrategy.ABSTRACT_MODIFIER) val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning(PositioningStrategy.TYPE_PARAMETERS_LIST) val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error(PositioningStrategy.TYPE_PARAMETERS_LIST) + val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) } val MPP_PROJECTS by object : DiagnosticGroup("Multi-platform projects") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt index 28d6b45a276..b7de21b6e51 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ComposedExpressionCheckers.kt @@ -23,6 +23,8 @@ class ComposedExpressionCheckers : ExpressionCheckers() { get() = _callCheckers override val functionCallCheckers: Set get() = _functionCallCheckers + override val propertyAccessExpressionCheckers: Set + get() = _propertyAccessExpressionCheckers override val integerLiteralOperatorCallCheckers: Set get() = _integerLiteralOperatorCallCheckers override val variableAssignmentCheckers: Set @@ -81,6 +83,7 @@ class ComposedExpressionCheckers : ExpressionCheckers() { private val _qualifiedAccessExpressionCheckers: MutableSet = mutableSetOf() private val _callCheckers: MutableSet = mutableSetOf() private val _functionCallCheckers: MutableSet = mutableSetOf() + private val _propertyAccessExpressionCheckers: MutableSet = mutableSetOf() private val _integerLiteralOperatorCallCheckers: MutableSet = mutableSetOf() private val _variableAssignmentCheckers: MutableSet = mutableSetOf() private val _tryExpressionCheckers: MutableSet = mutableSetOf() @@ -115,6 +118,7 @@ class ComposedExpressionCheckers : ExpressionCheckers() { _qualifiedAccessExpressionCheckers += checkers.qualifiedAccessExpressionCheckers _callCheckers += checkers.callCheckers _functionCallCheckers += checkers.functionCallCheckers + _propertyAccessExpressionCheckers += checkers.propertyAccessExpressionCheckers _integerLiteralOperatorCallCheckers += checkers.integerLiteralOperatorCallCheckers _variableAssignmentCheckers += checkers.variableAssignmentCheckers _tryExpressionCheckers += checkers.tryExpressionCheckers diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt index 5c72a5057e9..b9065d75a23 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt @@ -22,6 +22,7 @@ abstract class ExpressionCheckers { open val qualifiedAccessExpressionCheckers: Set = emptySet() open val callCheckers: Set = emptySet() open val functionCallCheckers: Set = emptySet() + open val propertyAccessExpressionCheckers: Set = emptySet() open val integerLiteralOperatorCallCheckers: Set = emptySet() open val variableAssignmentCheckers: Set = emptySet() open val tryExpressionCheckers: Set = emptySet() @@ -54,6 +55,7 @@ abstract class ExpressionCheckers { @CheckersComponentInternal internal val allQualifiedAccessExpressionCheckers: Set by lazy { qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers } @CheckersComponentInternal internal val allCallCheckers: Set by lazy { callCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allFunctionCallCheckers: Set by lazy { functionCallCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers + callCheckers } + @CheckersComponentInternal internal val allPropertyAccessExpressionCheckers: Set by lazy { propertyAccessExpressionCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers } @CheckersComponentInternal internal val allIntegerLiteralOperatorCallCheckers: Set by lazy { integerLiteralOperatorCallCheckers + functionCallCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers + callCheckers } @CheckersComponentInternal internal val allVariableAssignmentCheckers: Set by lazy { variableAssignmentCheckers + qualifiedAccessCheckers + basicExpressionCheckers } @CheckersComponentInternal internal val allTryExpressionCheckers: Set by lazy { tryExpressionCheckers + basicExpressionCheckers } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt index c04a232123e..6511fa142f1 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionCheckerAliases.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.fir.expressions.FirGetClassCall import org.jetbrains.kotlin.fir.expressions.FirIntegerLiteralOperatorCall import org.jetbrains.kotlin.fir.expressions.FirLoop import org.jetbrains.kotlin.fir.expressions.FirLoopJump +import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier @@ -47,6 +48,7 @@ typealias FirQualifiedAccessChecker = FirExpressionChecker typealias FirQualifiedAccessExpressionChecker = FirExpressionChecker typealias FirCallChecker = FirExpressionChecker typealias FirFunctionCallChecker = FirExpressionChecker +typealias FirPropertyAccessExpressionChecker = FirExpressionChecker typealias FirIntegerLiteralOperatorCallChecker = FirExpressionChecker typealias FirVariableAssignmentChecker = FirExpressionChecker typealias FirTryExpressionChecker = FirExpressionChecker 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 faa7cf40d3e..ea7c57c97af 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 @@ -552,6 +552,7 @@ object FirErrors { val ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS by error0(SourceElementPositioningStrategies.ABSTRACT_MODIFIER) val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error0(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST) + val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) // Multi-platform projects val EXPECTED_DECLARATION_WITH_BODY by error0(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 0b4a3a635c0..6a7173da933 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -64,6 +64,11 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirUnderscoredTypeArgumentSyntaxChecker, ) + override val propertyAccessExpressionCheckers: Set + get() = setOf( + FirPropertyAccessTypeArgumentsChecker, + ) + override val tryExpressionCheckers: Set get() = setOf( FirCatchParameterChecker diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirPropertyAccessTypeArgumentsChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirPropertyAccessTypeArgumentsChecker.kt new file mode 100644 index 00000000000..b182e151a2e --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirPropertyAccessTypeArgumentsChecker.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2022 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.expression + +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.FirPropertyAccessExpression +import org.jetbrains.kotlin.fir.references.FirErrorNamedReference + +object FirPropertyAccessTypeArgumentsChecker : FirPropertyAccessExpressionChecker() { + override fun check(expression: FirPropertyAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + // Property accesses may not have explicit type arguments (see KT-54978). Additionally, the callee reference's errors should take + // precedence, if any exist. For example, the callee reference might be a function `Collections.emptyList`, but the programmer + // has forgotten to add function call parentheses, making it a property access. + if (expression.calleeReference !is FirErrorNamedReference) { + val hasExplicitTypeArgument = expression.typeArguments.any { it.source != null } + if (hasExplicitTypeArgument) { + reporter.reportOn(expression.source, FirErrors.EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS, context) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt index 56ff3cd0c08..9e815a298db 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt @@ -41,7 +41,7 @@ class ExpressionCheckersDiagnosticComponent( } override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: CheckerContext) { - checkers.allQualifiedAccessExpressionCheckers.check(propertyAccessExpression, data) + checkers.allPropertyAccessExpressionCheckers.check(propertyAccessExpression, data) } override fun visitFunctionCall(functionCall: FirFunctionCall, data: CheckerContext) { 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 a445fd2d100..7a1b1e5e8ff 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 @@ -192,6 +192,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPLICIT_BACKING_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPLICIT_BACKING_FIELD_IN_EXTENSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPLICIT_BACKING_FIELD_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPLICIT_DELEGATION_CALL_REQUIRED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_FUNCTION_RETURN_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_PARAMETER_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_PROPERTY_TYPE @@ -1622,6 +1623,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { map.put(ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS, "This property cannot be declared abstract") map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING, "Type parameters for local variables are deprecated") map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS, "Local variables are not allowed to have type parameters") + map.put(EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS, "A property access cannot have explicit type arguments") map.put(CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT, "Const 'val' are only allowed on top level, in named objects, or in companion objects") map.put(CONST_VAL_WITH_GETTER, "Const 'val' should not have a getter") diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt index 6fd31355395..2f4780bc6e2 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.fir.kt @@ -17,17 +17,19 @@ class Test { val List.a: Int get() = size val List.b: Int? get() = size - fun List.testCallable1(): () -> Unit = a::foo + fun List.testCallable1(): () -> Unit = a::foo + fun List.testCallable1a(): () -> Unit = a::foo fun List.testCallable2(): () -> Unit = b?::foo - fun List.testCallable3(): () -> Unit = b::foo - fun List.testCallable4(): () -> Unit = b?::foo + fun List.testCallable3(): () -> Unit = b::foo + fun List.testCallable4(): () -> Unit = b?::foo - fun List.testClassLiteral1() = a::class + fun List.testClassLiteral1() = a::class + fun List.testClassLiteral1a() = a::class fun List.testClassLiteral2() = b?::class - fun List.testClassLiteral3() = b::class + fun List.testClassLiteral3() = b::class fun List.testUnresolved1() = unresolved::foo - fun List.testUnresolved2() = a<unresolved>::foo + fun List.testUnresolved2() = a<unresolved>::foo fun List.testUnresolved3() = a<>::foo fun List.testUnresolved4() = unresolved?::foo } diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt index 60573150638..03508a0acfd 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.kt @@ -17,12 +17,14 @@ class Test { val List.a: Int get() = size val List.b: Int? get() = size - fun List.testCallable1(): () -> Unit = a::foo + fun List.testCallable1(): () -> Unit = a::foo + fun List.testCallable1a(): () -> Unit = a::foo fun List.testCallable2(): () -> Unit = b?::foo fun List.testCallable3(): () -> Unit = b::foo fun List.testCallable4(): () -> Unit = b?::foo - fun List.testClassLiteral1() = a::class + fun List.testClassLiteral1() = a::class + fun List.testClassLiteral1a() = a::class fun List.testClassLiteral2() = b?::class fun List.testClassLiteral3() = b::class diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt index 2d8c50e20d8..b6c9589e112 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax.txt @@ -18,10 +18,12 @@ package test { public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public final fun kotlin.collections.List.testCallable1(): () -> kotlin.Unit + public final fun kotlin.collections.List.testCallable1a(): () -> kotlin.Unit public final fun kotlin.collections.List.testCallable2(): () -> kotlin.Unit public final fun kotlin.collections.List.testCallable3(): () -> kotlin.Unit public final fun kotlin.collections.List.testCallable4(): () -> kotlin.Unit public final fun kotlin.collections.List.testClassLiteral1(): kotlin.reflect.KClass + public final fun kotlin.collections.List.testClassLiteral1a(): kotlin.reflect.KClass public final fun kotlin.collections.List.testClassLiteral2(): kotlin.reflect.KClass public final fun kotlin.collections.List.testClassLiteral3(): kotlin.reflect.KClass public final fun kotlin.collections.List.testUnresolved1(): [Error type: Return type for function cannot be resolved] diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt index d862d9c81e5..42cc0877a3d 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.fir.kt @@ -21,8 +21,9 @@ class Test { val Int.c: Int get() = 42 - val test1: () -> Right = a.b.c::foo - val test1a: () -> Right = a.b.c::foo + val test1: () -> Right = a.b.c::foo + val test1a: () -> Right = a.b.c::foo - val test2: () -> 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/reservedExpressionSyntax3.kt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.kt index 2f31e39ee14..7ffa2ef0e6a 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.kt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.kt @@ -21,8 +21,9 @@ class Test { val Int.c: Int get() = 42 - val test1: () -> Right = a.b<Int>.c::foo - val test1a: () -> Right = a.b.c::foo + val test1: () -> Right = a.b.c::foo + val test1a: () -> Right = a.b<Int>.c::foo - val test2: () -> Right = a.b<Int>.c?::foo + val test2: () -> Right = a.b.c?::foo + val test2a: () -> Right = a.b<Int>.c?::foo } diff --git a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.txt b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.txt index 41f98de3f0f..11236fc3892 100644 --- a/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.txt +++ b/compiler/testData/diagnostics/tests/callableReference/bound/reservedExpressionSyntax3.txt @@ -16,6 +16,7 @@ package test { public final val test1: () -> test.Right public final val test1a: () -> test.Right public final val test2: () -> test.Right + public final val test2a: () -> test.Right public final val kotlin.collections.List.b: kotlin.Int public final val kotlin.Int.c: kotlin.Int public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean diff --git a/compiler/testData/diagnostics/tests/syntax/complicatedLTGT.fir.kt b/compiler/testData/diagnostics/tests/syntax/complicatedLTGT.fir.kt index 7fe44c53a54..e775b521c75 100644 --- a/compiler/testData/diagnostics/tests/syntax/complicatedLTGT.fir.kt +++ b/compiler/testData/diagnostics/tests/syntax/complicatedLTGT.fir.kt @@ -2,7 +2,7 @@ // ISSUE: KT-8263 fun test(x: Int, y: Int) { - if (x < (if (y > 115) 1 else 2)) { + if (x < (if (y > 115) 1 else 2)) { Unit } }