From 47407c44456294900ea75ae31f4d3dfba6527da1 Mon Sep 17 00:00:00 2001 From: Andrey Zinovyev Date: Sun, 11 Apr 2021 16:18:07 +0000 Subject: [PATCH] [FIR] Add NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER check --- ...TouchedTilContractsPhaseTestGenerated.java | 5 + .../diagnostics/multipleBounds.fir.txt | 109 ++++++++++++++++++ .../resolve/diagnostics/multipleBounds.kt | 69 +++++++++++ .../runners/FirDiagnosticTestGenerated.java | 6 + ...DiagnosticsWithLightTreeTestGenerated.java | 6 + .../diagnostics/FirDiagnosticsList.kt | 10 +- .../fir/analysis/diagnostics/FirErrors.kt | 2 + .../declaration/FirTypeConstraintsChecker.kt | 33 ++++++ .../diagnostics/FirDefaultErrorMessages.kt | 8 ++ .../fir/checkers/CommonDeclarationCheckers.kt | 1 + .../converter/DeclarationsConverter.kt | 34 +++++- .../fir/lightTree/fir/TypeConstraint.kt | 8 +- .../kotlin/fir/builder/RawFirBuilder.kt | 25 ++++ .../fir/declarations/danglingConstraints.kt | 30 +++++ .../diagnostics/tests/MultipleBounds.fir.kt | 6 +- .../fir/generator/HLDiagnosticConverter.kt | 10 +- .../diagnostics/KtFirDataClassConverters.kt | 8 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 6 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 9 ++ idea/testData/checker/MultipleBounds.fir.kt | 4 +- 20 files changed, 373 insertions(+), 16 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeConstraintsChecker.kt create mode 100644 compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/danglingConstraints.kt 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 55dba6b18d2..261cc2fea08 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 @@ -1150,6 +1150,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/methodOfAnyImplementedInInterface.kt"); } + @TestMetadata("multipleBounds.kt") + public void testMultipleBounds() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt"); + } + @TestMetadata("nonConstValInAnnotationArgument.kt") public void testNonConstValInAnnotationArgument() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/nonConstValInAnnotationArgument.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.fir.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.fir.txt new file mode 100644 index 00000000000..8664ca97613 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.fir.txt @@ -0,0 +1,109 @@ +FILE: multipleBounds.kt + public open class A : R|kotlin/Any| { + public constructor(): R|Jet87/A| { + super() + } + + public final fun foo(): R|kotlin/Int| { + ^foo Int(1) + } + + } + public abstract interface B : R|kotlin/Any| { + public open fun bar(): R|kotlin/Double| { + ^bar Double(1.0) + } + + } + public abstract interface G : R|kotlin/Any| { + public abstract val boo: R|kotlin/Double| + public get(): R|kotlin/Double| + + public abstract val bal: R|kotlin/Double| + public get(): R|kotlin/Double| + + public abstract val bas: R|kotlin/Double| + public get(): R|kotlin/Double| + + } + public final class C : R|Jet87/A|, R|Jet87/B| { + public constructor(): R|Jet87/C| { + super() + } + + } + public final class D : R|kotlin/Any| { + public constructor(): R|Jet87/D| { + super() + } + + public final companion object Companion : R|Jet87/A|, R|Jet87/B| { + private constructor(): R|Jet87/D.Companion| { + super() + } + + } + + } + public final class Test1 : R|kotlin/Any| { + public constructor(): R|Jet87/Test1| { + super() + } + + public final fun test(t: R|T|): R|kotlin/Unit| { + R|?|.#() + R|?|.#() + R|/t|.R|Jet87/A.foo|() + R|/t|.R|Jet87/B.bar|() + } + + } + public final fun test(): R|kotlin/Unit| { + R|Jet87/Test1.Test1|() + R|Jet87/Test1.Test1|() + R|Jet87/Test1.Test1|() + } + public final class Foo : R|kotlin/Any| { + public constructor(): R|Jet87/Foo| { + super() + } + + } + public final class Bar : R|kotlin/Any| { + public constructor(): R|Jet87/Bar| { + super() + } + + } + public final class Buzz|, > : R|kotlin/Any| { + public constructor|, >(): R|Jet87/Buzz| { + super() + } + + } + public final class X : R|kotlin/Any| { + public constructor(): R|Jet87/X| { + super() + } + + } + public final class Y|> : R|kotlin/Any| { + public constructor|>(): R|Jet87/Y| { + super() + } + + } + public final fun test2(t: R|T|): R|kotlin/Unit| { + R|?|.#() + R|?|.#() + R|/t|.R|Jet87/A.foo|() + R|/t|.R|Jet87/B.bar|() + } + public final val t1: R|kotlin/Unit| = #(R|Jet87/A.A|()) + public get(): R|kotlin/Unit| + public final val t2: R|kotlin/Unit| = #(R|Jet87/C.C|()) + public get(): R|kotlin/Unit| + public final val t3: R|kotlin/Unit| = R|Jet87/test2|(R|Jet87/C.C|()) + public get(): R|kotlin/Unit| + public final val x: R|kotlin/Int| = Int(0) + public get(): R|kotlin/Int| diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt new file mode 100644 index 00000000000..529a97b9729 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt @@ -0,0 +1,69 @@ +package Jet87 + +open class A() { + fun foo() : Int = 1 +} + +interface B { + fun bar() : Double = 1.0; +} + +interface G { + val boo: Double where X : A, X : B + val bal: Double where A : B + val bas: Double where Y : B, X : B +} + +class C() : A(), B + +class D() { + companion object : A(), B {} +} + +class Test1() + where + T : A, + T : B, + B : T // error + { + + fun test(t : T) { + T.foo() + T.bar() + t.foo() + t.bar() + } +} + +fun test() { + Test1<B>() + Test1<A>() + Test1() +} + +class Foo() {} + +class BarFoo> + +class Buzz where T : Bar, T : nioho + +class XFoo> +class Y<T> where T : Foo, T : Bar + +fun test2(t : T) + where + T : A, + T : B, + B : T +{ + T.foo() + T.bar() + t.foo() + t.bar() +} + +val t1 = test2(A()) +val t2 = test2(C()) +val t3 = test2(C()) + +val x : Int = 0 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 ebb2ea80093..4bf14320b0c 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 @@ -1321,6 +1321,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/methodOfAnyImplementedInInterface.kt"); } + @Test + @TestMetadata("multipleBounds.kt") + public void testMultipleBounds() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt"); + } + @Test @TestMetadata("nonConstValInAnnotationArgument.kt") public void testNonConstValInAnnotationArgument() 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 b11de9cac1e..41b38bbd84a 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 @@ -1333,6 +1333,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/methodOfAnyImplementedInInterface.kt"); } + @Test + @TestMetadata("multipleBounds.kt") + public void testMultipleBounds() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/multipleBounds.kt"); + } + @Test @TestMetadata("nonConstValInAnnotationArgument.kt") public void testNonConstValInAnnotationArgument() throws Exception { 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 9bfc89c691d..2d316ad4403 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 @@ -16,10 +16,7 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.PrivateForInline -import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration -import org.jetbrains.kotlin.fir.declarations.FirClass -import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration -import org.jetbrains.kotlin.fir.declarations.FirValueParameter +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol @@ -309,6 +306,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val CONFLICTING_UPPER_BOUNDS by error { parameter("typeParameter") } + + val NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER by error { + parameter("typeParameterName") + parameter("typeParametersOwner") + } } val REFLECTION by object : DiagnosticGroup("Reflection") { 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 c631fae1e36..6b02d38c8f5 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 @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStrategies import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.FirExpression @@ -226,6 +227,7 @@ object FirErrors { val ONLY_ONE_CLASS_BOUND_ALLOWED by error0() val REPEATED_BOUND by error0() val CONFLICTING_UPPER_BOUNDS by error1() + val NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER by error2() // Reflection val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeConstraintsChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeConstraintsChecker.kt new file mode 100644 index 00000000000..cbef71d0cc1 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeConstraintsChecker.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner +import org.jetbrains.kotlin.fir.declarations.getDanglingTypeConstraintsOrEmpty + +object FirTypeConstraintsChecker : FirBasicDeclarationChecker() { + + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration !is FirTypeParameterRefsOwner) return + + //basically we transfer errors, which were discovered in ast parsers + declaration.getDanglingTypeConstraintsOrEmpty().forEach { constraint -> + reporter.reportOn( + constraint.source, + FirErrors.NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER, + constraint.name, + declaration, + context + ) + } + } + +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 5d34a2b52e1..44177fe9744 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -136,6 +136,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAMED_ARGUMENTS_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAMED_PARAMETER_NOT_FOUND +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NESTED_CLASS_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NONE_APPLICABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY @@ -474,6 +475,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { SYMBOL ) + map.put( + NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER, + "{0} does not refer to a type parameter of {1}", + TO_STRING, + NAME + ) + // Reflection map.put( EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt index 4f1a393aa15..75d3953cbbd 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt @@ -18,6 +18,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirModifierChecker, FirConflictsChecker, FirConflictingProjectionChecker, + FirTypeConstraintsChecker, ) override val memberDeclarationCheckers: Set = setOf( diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt index 34d1eda5104..f9690cb3fac 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt @@ -526,6 +526,7 @@ class DeclarationsConverter( } }.also { it.initContainingClassForLocalAttr() + fillDanglingConstraintsTo(firTypeParameters, typeConstraints, it) } } @@ -1083,6 +1084,8 @@ class DeclarationsConverter( } } annotations += modifiers.annotations + }.also { + fillDanglingConstraintsTo(firTypeParameters, typeConstraints, it) } } @@ -1394,6 +1397,9 @@ class DeclarationsConverter( context.firFunctionTargets.removeLast() }.build().also { target.bind(it) + if (it is FirSimpleFunction) { + fillDanglingConstraintsTo(firTypeParameters, typeConstraints, it) + } } } @@ -1573,17 +1579,21 @@ class DeclarationsConverter( private fun convertTypeConstraint(typeConstraint: LighterASTNode): TypeConstraint { lateinit var identifier: String lateinit var firType: FirTypeRef + lateinit var referenceExpression: LighterASTNode val annotations = mutableListOf() typeConstraint.forEachChildren { when (it.tokenType) { //annotations will be saved later, on mapping stage with type parameters ANNOTATION, ANNOTATION_ENTRY -> annotations += convertAnnotation(it) - REFERENCE_EXPRESSION -> identifier = it.asText + REFERENCE_EXPRESSION -> { + identifier = it.asText + referenceExpression = it + } TYPE_REFERENCE -> firType = convertType(it) } } - return TypeConstraint(annotations, identifier, firType) + return TypeConstraint(annotations, identifier, firType, referenceExpression.toFirSourceElement()) } /** @@ -1867,4 +1877,24 @@ class DeclarationsConverter( } calleeReference = FirReferencePlaceholderForResolvedAnnotations } + + private fun fillDanglingConstraintsTo( + typeParameters: List, + typeConstraints: List, + to: T + ) where T : FirDeclaration, T : FirTypeParameterRefsOwner { + val typeParamNames = typeParameters.map { it.name }.toSet() + val result = typeConstraints.mapNotNull { constraint -> + val name = constraint.identifier.nameAsSafeName() + if (!typeParamNames.contains(name)) { + DanglingTypeConstraint(name, constraint.source) + } else { + null + } + + } + if (result.isNotEmpty()) { + to.danglingTypeConstraints = result + } + } } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/TypeConstraint.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/TypeConstraint.kt index 383932f0438..b6dfc3e20ce 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/TypeConstraint.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/TypeConstraint.kt @@ -5,7 +5,13 @@ package org.jetbrains.kotlin.fir.lightTree.fir +import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.types.FirTypeRef -class TypeConstraint(val annotations: List, val identifier: String, val firTypeRef: FirTypeRef) \ No newline at end of file +class TypeConstraint( + val annotations: List, + val identifier: String, + val firTypeRef: FirTypeRef, + val source: FirSourceElement +) diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index 954ba16a973..ba7ed885f91 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -500,6 +500,23 @@ open class RawFirBuilder( } } + private fun KtTypeParameterListOwner.fillDanglingConstraintsTo(to: T) where T : FirDeclaration, T : FirTypeParameterRefsOwner { + val typeParamNames = typeParameters.mapNotNull { it.nameAsName }.toSet() + val result = typeConstraints.mapNotNull { constraint -> + constraint.subjectTypeParameterName?.getReferencedNameAsName()?.let { name -> + if (!typeParamNames.contains(name)) { + DanglingTypeConstraint(name, constraint.subjectTypeParameterName!!.toFirSourceElement()) + } else { + null + } + } + + } + if (result.isNotEmpty()) { + to.danglingTypeConstraints = result + } + } + private fun KtDeclarationWithBody.extractValueParametersTo( container: FirFunctionBuilder, defaultTypeRef: FirTypeRef? = null, @@ -898,6 +915,7 @@ open class RawFirBuilder( } }.also { it.initContainingClassForLocalAttr() + classOrObject.fillDanglingConstraintsTo(it) } } @@ -1030,6 +1048,9 @@ open class RawFirBuilder( context.firFunctionTargets.removeLast() }.build().also { target.bind(it) + if (it is FirSimpleFunction) { + function.fillDanglingConstraintsTo(it) + } } } @@ -1308,6 +1329,10 @@ open class RawFirBuilder( } } extractAnnotationsTo(this) + }.also { + if (!isLocal) { + fillDanglingConstraintsTo(it) + } } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/danglingConstraints.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/danglingConstraints.kt new file mode 100644 index 00000000000..1456801e147 --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/danglingConstraints.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2021 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.declarations + +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.name.Name + +/** + * Constraint without corresponding type argiment + */ +data class DanglingTypeConstraint(val name: Name, val source: FirSourceElement) + +private object DanglingTypeConstraintsKey : FirDeclarationDataKey() + +var T.danglingTypeConstraints: List? + where T : FirDeclaration, T : FirTypeParameterRefsOwner + by FirDeclarationDataRegistry.data(DanglingTypeConstraintsKey) + +fun FirDeclaration.getDanglingTypeConstraintsOrEmpty(): List { + val res = when (this) { + is FirRegularClass -> danglingTypeConstraints + is FirSimpleFunction -> danglingTypeConstraints + is FirProperty -> danglingTypeConstraints + else -> null + } + return res ?: emptyList() +} diff --git a/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt b/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt index 936c0609d51..529a97b9729 100644 --- a/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt +++ b/compiler/testData/diagnostics/tests/MultipleBounds.fir.kt @@ -11,7 +11,7 @@ interface B { interface G { val boo: Double where X : A, X : B val bal: Double where A : B - val bas: Double where Y : B, X : B + val bas: Double where Y : B, X : B } class C() : A(), B @@ -24,7 +24,7 @@ class Test1() where T : A, T : B, - B : T // error + B : T // error { fun test(t : T) { @@ -54,7 +54,7 @@ fun test2(t : T) where T : A, T : B, - B : T + B : T { T.foo() T.bar() diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt index d3a38709047..4cb373b7718 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt @@ -15,10 +15,7 @@ import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.DiagnosticData import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.DiagnosticList import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.DiagnosticParameter -import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration -import org.jetbrains.kotlin.fir.declarations.FirClass -import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration -import org.jetbrains.kotlin.fir.declarations.FirValueParameter +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol @@ -183,6 +180,11 @@ private object FirToKtConversionCreator { KtVariableLikeSymbol::class.createType(), importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirVariable") ), + FirDeclaration::class to HLFunctionCallConversion( + "firSymbolBuilder.buildSymbol({0})", + KtSymbol::class.createType(), + importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration") + ), ) private val allowedTypesWithoutTypeParams = setOf( diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 89c964d3546..f1d6be22a54 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -945,6 +945,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER) { firDiagnostic -> + NameInConstraintIsNotATypeParameterImpl( + firDiagnostic.a, + firSymbolBuilder.buildSymbol(firDiagnostic.b), + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic -> ExtensionInClassReferenceNotAllowedImpl( firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 86db3e71b3a..dbdb8953e22 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -673,6 +673,12 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val typeParameter: KtTypeParameterSymbol } + abstract class NameInConstraintIsNotATypeParameter : KtFirDiagnostic() { + override val diagnosticClass get() = NameInConstraintIsNotATypeParameter::class + abstract val typeParameterName: Name + abstract val typeParametersOwner: KtSymbol + } + abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic() { override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class abstract val referencedDeclaration: KtCallableSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index bd40bccbdf2..9edc330ed87 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1085,6 +1085,15 @@ internal class ConflictingUpperBoundsImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class NameInConstraintIsNotATypeParameterImpl( + override val typeParameterName: Name, + override val typeParametersOwner: KtSymbol, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.NameInConstraintIsNotATypeParameter(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ExtensionInClassReferenceNotAllowedImpl( override val referencedDeclaration: KtCallableSymbol, firDiagnostic: FirPsiDiagnostic<*>, diff --git a/idea/testData/checker/MultipleBounds.fir.kt b/idea/testData/checker/MultipleBounds.fir.kt index 60df6261d98..a66fb827ef7 100644 --- a/idea/testData/checker/MultipleBounds.fir.kt +++ b/idea/testData/checker/MultipleBounds.fir.kt @@ -18,7 +18,7 @@ class Test1() where T : A, T : B, - B : T // error + B : T // error { fun test(t : T) { @@ -48,7 +48,7 @@ fun test2(t : T) where T : A, T : B, - B : T + B : T { T.foo() T.bar()