diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt new file mode 100644 index 00000000000..61ce0cd61a4 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt @@ -0,0 +1,21 @@ +// FILE: usage.kt + +val propertyToResolve: String + get() = JavaClass.function()?.let { " ($it)" } ?: "" + + +// FILE: Anno.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface Anno { + String value(); +} + +// FILE: JavaClass.java +public class JavaClass { + public static @Anno("outer") List<@Anno("middle") List<@Anno("inner") Integer>> function() { + return null; + } +} diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt new file mode 100644 index 00000000000..661eb76d01e --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.out_of_src_roots.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#()?.{ $subj$.#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) } ?: String() + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#()?.{ $subj$.#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) } ?: String() + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt new file mode 100644 index 00000000000..f364c23cf8a --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|()?.{ $subj$.R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) } ?: String() + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|()?.{ $subj$.R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) } ?: String() + } diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt new file mode 100644 index 00000000000..a14b293a995 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt @@ -0,0 +1,21 @@ +// FILE: usage.kt + +val propertyToResolve: String + get() = JavaClass.function().let { " ($it)" } + + +// FILE: Anno.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface Anno { + String value(); +} + +// FILE: JavaClass.java +public class JavaClass { + public static @Anno("outer") List<@Anno("middle") List<@Anno("inner") Integer>> function() { + return null; + } +} diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt new file mode 100644 index 00000000000..bd24680a314 --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.out_of_src_roots.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#().#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ #.#().#( = [ResolvedTo(BODY_RESOLVE)] let@fun (): R|kotlin/String| { + ^ (String( (), #, String())) + } + ) + } \ No newline at end of file diff --git a/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt new file mode 100644 index 00000000000..6e8e7b0e79c --- /dev/null +++ b/analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.txt @@ -0,0 +1,23 @@ +KT element: KtProperty +FIR element: FirPropertyImpl +FIR source kind: KtRealSourceElementKind + +FIR element rendered: +public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|().R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List!|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) + } + +FIR FILE: +FILE: [ResolvedTo(IMPORTS)] usage.kt + [ResolvedTo(BODY_RESOLVE)] annotations container + public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| + public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { + ^ Q|JavaClass|.R|/JavaClass.function|().R|kotlin/let|( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(value = String(outer)) List!|): R|kotlin/String| { + ^ (String( (), R|/it|, String())) + } + ) + } diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt index 7d37fe86459..7a2b775f5f7 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter.txt @@ -229,8 +229,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameter.kt public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| { ^ Null(null) } public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt index dc186d1d84f..7032ccfe0cc 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameter2.txt @@ -229,13 +229,13 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameter2.kt public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(outer)) kotlin/collections/List<@R|Anno|(String(middle)) kotlin/collections/List<@R|Anno|(String(inner)) kotlin/Int>>?| { ^ Null(null) } public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/String| { - ^ R|/nullablePropertyWithAnnotatedType|.R|kotlin/let|>?|, R|kotlin/String|>( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?|): R|kotlin/String| { + ^ R|/nullablePropertyWithAnnotatedType|.R|kotlin/let|>?|, R|kotlin/String|>( = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1] let@fun ([ResolvedTo(BODY_RESOLVE)] it: R|@R|Anno|(message = String(outer)) kotlin/collections/List<@R|Anno|(message = String(middle)) kotlin/collections/List<@R|Anno|(message = String(inner)) kotlin/Int>>?|): R|kotlin/String| { ^ (String( (), R|/it|, String())) } ) diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt index 93a3bde62a5..73d7c66c567 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/anonymousFunctionWithAnnotatedParameterOnImplicitTypePhase.txt @@ -185,8 +185,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameterOnImplicitTyp public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(str)) kotlin/String?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(str)) kotlin/String?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(str)) kotlin/String?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(str)) kotlin/String?| { ^ Null(null) } public final [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| @@ -209,8 +209,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameterOnImplicitTyp public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(str)) kotlin/String?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(str)) kotlin/String?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(str)) kotlin/String?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(str)) kotlin/String?| { ^ Null(null) } public final [ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] val propertyToResolve: R|kotlin/String| @@ -233,8 +233,8 @@ FILE: [ResolvedTo(IMPORTS)] anonymousFunctionWithAnnotatedParameterOnImplicitTyp public [ResolvedTo(STATUS)] [ContainingClassKey=Anno] get(): R|kotlin/String| } - public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(message = String(str)) kotlin/String?| - public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(message = String(str)) kotlin/String?| { + public final [ResolvedTo(CONTRACTS)] val nullablePropertyWithAnnotatedType: R|@R|Anno|(String(str)) kotlin/String?| + public [ResolvedTo(CONTRACTS)] get(): R|@R|Anno|(String(str)) kotlin/String?| { ^ Null(null) } public final [ResolvedTo(BODY_RESOLVE)] val propertyToResolve: R|kotlin/String| diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java index 7e256fba4e4..328af533d94 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/OutOfContentRootGetOrBuildFirTestGenerated.java @@ -1159,6 +1159,18 @@ public class OutOfContentRootGetOrBuildFirTestGenerated extends AbstractOutOfCon runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/primaryConstructorValueParameter.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt"); + } + + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction2.kt") + public void testPropagateFlexibleTypeToAnonymousFunction2() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt"); + } + @Test @TestMetadata("secondaryConstructorValueParameter.kt") public void testSecondaryConstructorValueParameter() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java index d341d16c269..8cf04279506 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/SourceGetOrBuildFirTestGenerated.java @@ -1159,6 +1159,18 @@ public class SourceGetOrBuildFirTestGenerated extends AbstractSourceGetOrBuildFi runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/primaryConstructorValueParameter.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction.kt"); + } + + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction2.kt") + public void testPropagateFlexibleTypeToAnonymousFunction2() throws Exception { + runTest("analysis/low-level-api-fir/testdata/getOrBuildFir/wholeDeclaration/propagateFlexibleTypeToAnonymousFunction2.kt"); + } + @Test @TestMetadata("secondaryConstructorValueParameter.kt") public void testSecondaryConstructorValueParameter() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index e02bde46145..34bbd529a1e 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -20258,6 +20258,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 3533d26cb9c..4d02df81343 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -20258,6 +20258,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 5e3f413694a..ce6772baf3b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -20258,6 +20258,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 351d2c2f9dd..695e05c065d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -20264,6 +20264,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 632cfdc7db2..567f7478bac 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -100,6 +100,73 @@ fun ConeDefinitelyNotNullType.Companion.create( fun ConeDynamicType.Companion.create(session: FirSession): ConeDynamicType = ConeDynamicType(session.builtinTypes.nothingType.type, session.builtinTypes.nullableAnyType.type) +/** + * This call is required if you want to use a type with annotations that are linked to a declaration from this declaration inside another + * to avoid concurrent modification problems (see KT-60387). + * + * Otherwise, those annotations can be transformed from different threads simultaneously that is error-prone. + * Example: + * ```kotlin + * val declaration: @Anno("outer") Int? get() = null + * val anotherDeclaration: String get() = declaration?.let { "$it" }.toString() + * ``` + * here for `declaration?.let { "$it" }` will be created an anonymous function with argument that + * will have the same type as the return type of `declaration`. + * So, now we have 2 possible scenarios: + * + * Wrong case 1 – The type was inserted to a new type ref as it is. + * This means that a type reference from `declaration` and from the anonymous lambda will have the same instance of `Anno`. + * Now we resole `anotherDeclaration` to [FirResolvePhase.BODY_RESOLVE] phase + * and as a result we will have resolved annotation arguments in the type. + * At the same time, `declaration` can be still in [FirResolvePhase.CONTRACTS] phase, because no one called a resolution yet. + * So now imagine a situation when one thread (1) wants to read annotation arguments from the fully resolved anonymous function, + * and another thread (2) wants to resolve `declaration` to [FirResolvePhase.ANNOTATIONS_ARGUMENTS_MAPPING] phase. + * As a result, we will have a moment there thread 2 will replace arguments with a lazy expression + * to be sure that we will have a safe basis for resolution (see StateKeeper concept in LL FIR). + * And thread 1 will see unexpected unresolved annotation arguments in fully resolved function, because those type references + * use the same instance of the type (`FirArgumentListImpl` cannot be cast to `FirResolvedArgumentList`). + * So we lost here. + * + * Right case 2 – The type was "copied" by [independentInstance]. + * In this case, `declaration` and `anotherDeclaration` will have different instances of one `@Anno("outer") Int?` type. + * This means that we can't come to a situation where we can modify a fully resolved annotation from another thread. + * + * @return an instance of a type that has no annotations associated with any declaration, + * so it won't be changed from LL FIR lazy transformers concurrently + * + * @see CustomAnnotationTypeAttribute.independentInstance + */ +fun ConeKotlinType.independentInstance(): ConeKotlinType = instanceWithIndependentArguments().instanceWithIndependentAnnotations() + +private fun ConeKotlinType.instanceWithIndependentArguments(): ConeKotlinType { + val typeProjections = typeArguments + if (typeProjections.isEmpty()) return this + + var argumentsChanged = false + val newArguments = type.typeArguments.map { originalArgument -> + if (originalArgument !is ConeKotlinType) + originalArgument + else + originalArgument.independentInstance().also { + if (it !== originalArgument) { + argumentsChanged = true + } + } + } + + return if (argumentsChanged) withArguments(newArguments.toTypedArray()) else this +} + +private fun ConeKotlinType.instanceWithIndependentAnnotations(): ConeKotlinType { + val custom = attributes.custom ?: return this + val newAnnotations = custom.independentInstance() + if (newAnnotations === custom) { + return this + } + + val newAttributes = attributes.remove(custom).plus(newAnnotations) + return withAttributes(newAttributes) +} fun ConeKotlinType.makeConeTypeDefinitelyNotNullOrNotNull( typeContext: ConeTypeContext, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index afe5c433e95..a23dd5e2b0b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -481,9 +481,8 @@ fun FirCheckedSafeCallSubject.propagateTypeFromOriginalReceiver( .coneTypeSafe() ?: return val expandedReceiverType = receiverType.fullyExpandedType(session) - - val resolvedTypeRef = - typeRef.resolvedTypeFromPrototype(expandedReceiverType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext)) + val updatedReceiverType = expandedReceiverType.makeConeTypeDefinitelyNotNullOrNotNull(session.typeContext).independentInstance() + val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(updatedReceiverType) replaceTypeRef(resolvedTypeRef) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, file.source) } @@ -506,7 +505,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck( } } - val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(resultingType) + val resolvedTypeRef = typeRef.resolvedTypeFromPrototype(resultingType.independentInstance()) replaceTypeRef(resolvedTypeRef) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, file.source) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt index f4b23607f19..df6b3a7c173 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/PostponedArgumentsAnalyzer.kt @@ -118,7 +118,7 @@ class PostponedArgumentsAnalyzer( val stubsForPostponedVariables = c.bindingStubsForPostponedVariables() val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) }) - fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType + fun substitute(type: ConeKotlinType) = (currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType).independentInstance() val receiver = lambda.receiver?.let(::substitute) val contextReceivers = lambda.contextReceivers.map(::substitute) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt index 43eb2bc9159..d81dd59db68 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/CustomAnnotationTypeAttribute.kt @@ -6,10 +6,33 @@ package org.jetbrains.kotlin.fir.types import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCallCopy +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCopy import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import kotlin.reflect.KClass +/** + * @param containerSymbols a list of symbols that should be resolved to make [annotations] are fully resolved. + * Required only for "lazy" resolve mode in AA FIR to make a type annotation lazily resolved. + * See KtFirAnnotationListForType for reference. + * Example: + * ```kotlin + * fun foo(): @Anno Type + * ``` + * This `Anno` annotation will have `foo` function as [containerSymbols]. + * More than one [containerSymbols] possible in case of type aliases: + * ```kotlin + * interface BaseInterface + * typealias FirstTypeAlias = @Anno1 BaseInterface + * typealias SecondTypeAlias = @Anno2 FirstTypeAlias + * + * fun foo(): @Anno3 SecondTypeAlias = TODO() + * ``` + * here `@Anno3 SecondTypeAlias` will be expanded to ` @Anno1 @Anno2 @Anno3 BaseInterface` + * and will have all intermediate type-aliases as [containerSymbols]. + */ class CustomAnnotationTypeAttribute( val annotations: List, val containerSymbols: List> = emptyList(), @@ -34,6 +57,27 @@ class CustomAnnotationTypeAttribute( override val key: KClass get() = CustomAnnotationTypeAttribute::class + + /** + * Return an instance of the attribute that is not linked to any [containerSymbols]. + * It is required to avoid concurrent modification of those annotations from the linked + * declaration and another call site (e.g., if a type was propagated to an anonymous function). + * + * See KT-60387 as an example of a possible concurrent problem. + */ + fun independentInstance(): CustomAnnotationTypeAttribute = if (containerSymbols.isEmpty()) { + this + } else { + CustomAnnotationTypeAttribute( + annotations = annotations.map { + if (it is FirAnnotationCall) { + buildAnnotationCallCopy(it) {} + } else { + buildAnnotationCopy(it) {} + } + } + ) + } } val ConeAttributes.custom: CustomAnnotationTypeAttribute? by ConeAttributes.attributeAccessor() diff --git a/compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt b/compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt new file mode 100644 index 00000000000..0d46abf3375 --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// FILE: usage.kt +val propertyToResolve: String + get() = JavaClass.function()?.let { " ($it)" } ?: "" + +// FILE: Anno.java +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface Anno { + String value(); +} + +// FILE: JavaClass.java +import java.util.List; + +public class JavaClass { + public static @Anno("outer") List<@Anno("middle") List<@Anno("inner") Integer>> function() { + return null; + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 42175e8ec51..82f83fd1262 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -20264,6 +20264,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/j+k/privatePropertyAndSetterMultiModule.kt"); } + @Test + @TestMetadata("propagateFlexibleTypeToAnonymousFunction.kt") + public void testPropagateFlexibleTypeToAnonymousFunction() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/propagateFlexibleTypeToAnonymousFunction.kt"); + } + @Test @TestMetadata("protectedStaticSamePackage.kt") public void testProtectedStaticSamePackage() throws Exception {