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 {