[FIR] fold string literals concatenation API-ANALYSIS
diff --git a/analysis/analysis-api/testData/references/isReferenceTo/topLevelInfixFunctionCall.kt b/analysis/analysis-api/testData/references/isReferenceTo/topLevelInfixFunctionCall.kt
index 2ed847f..0cc35a8 100644
--- a/analysis/analysis-api/testData/references/isReferenceTo/topLevelInfixFunctionCall.kt
+++ b/analysis/analysis-api/testData/references/isReferenceTo/topLevelInfixFunctionCall.kt
@@ -5,5 +5,5 @@
 // FILE: UseSite.kt
 
 fun test() {
-    0 <caret>customPlus 0
+    "1" <caret>+ "2"
 }
\ No newline at end of file
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt
index d4f61a4..fad75b0 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt
@@ -18,11 +18,19 @@
 import org.jetbrains.kotlin.fir.declarations.*
 import org.jetbrains.kotlin.fir.declarations.utils.correspondingValueParameterFromPrimaryConstructor
 import org.jetbrains.kotlin.fir.expressions.FirAnnotation
+import org.jetbrains.kotlin.fir.expressions.FirStringConcatenationCall
+import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
 import org.jetbrains.kotlin.fir.resolve.providers.firProvider
+import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
 import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
 import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhaseRecursively
+import org.jetbrains.kotlin.name.StandardClassIds
 import org.jetbrains.kotlin.psi.*
 import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
+import org.jetbrains.kotlin.toKtPsiSourceElement
+import org.jetbrains.kotlin.util.OperatorNameConventions
 import org.jetbrains.kotlin.utils.ThreadSafe
 import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
 import org.jetbrains.kotlin.utils.exceptions.withPsiEntry
@@ -121,7 +129,24 @@
 
         val structureElement = fileStructure.getStructureElementFor(element, nonLocalContainer)
         val mappings = structureElement.mappings
-        return mappings.getFir(psi)
+        return adjustSelectedFir(mappings.getFir(psi), element)
+    }
+
+    private fun adjustSelectedFir(fir: FirElement?, element: KtElement): FirElement? {
+        if (fir is FirStringConcatenationCall && fir.fromStringLiterals && element is KtOperationReferenceExpression) {
+            val stringClassSymbol =
+                moduleComponents.session.symbolProvider.getClassLikeSymbolByClassId(StandardClassIds.String) as FirRegularClassSymbol
+            val plusStringSymbol =
+                stringClassSymbol.declarationSymbols.find { it is FirFunctionSymbol && it.callableId.callableName == OperatorNameConventions.PLUS }!!
+
+            return buildResolvedNamedReference {
+                source = element.toKtPsiSourceElement()
+                name = OperatorNameConventions.PLUS
+                resolvedSymbol = plusStringSymbol
+            }
+        }
+
+        return fir
     }
 
     private inline fun <T : KtElement, E : PsiElement> getFirForNonBodyElement(
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt
index 3657e92..0c56fa1 100644
--- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt
+++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/file/structure/FirElementsRecorder.kt
@@ -9,6 +9,7 @@
 import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.DuplicatedFirSourceElementsException
 import org.jetbrains.kotlin.analysis.low.level.api.fir.util.isErrorElement
 import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.FirImplementationDetail
 import org.jetbrains.kotlin.fir.builder.toFirOperationOrNull
 import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
 import org.jetbrains.kotlin.fir.expressions.*
@@ -23,6 +24,7 @@
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.psi.*
 import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
+import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
 import org.jetbrains.kotlin.types.ConstantValueKind
 import org.jetbrains.kotlin.util.OperatorNameConventions
 
@@ -72,6 +74,20 @@
         super.visitTypeParameter(typeParameter, data)
     }
 
+    @OptIn(FirImplementationDetail::class)
+    override fun visitStringConcatenationCall(
+        stringConcatenationCall: FirStringConcatenationCall,
+        data: MutableMap<KtElement, FirElement>,
+    ) {
+        if (stringConcatenationCall.fromStringLiterals) {
+            stringConcatenationCall.psi?.forEachDescendantOfType<KtBinaryExpression> {
+                data.put(it, stringConcatenationCall)
+            }
+        }
+
+        super.visitStringConcatenationCall(stringConcatenationCall, data)
+    }
+
     override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: MutableMap<KtElement, FirElement>) {
         // For the LHS of the assignment, record the assignment itself
         (variableAssignment.lValue.source?.psi as? KtElement)?.let { cache(it, variableAssignment, data) }
diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt
index bb354f9..21f1398 100644
--- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt
+++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt
@@ -388,6 +388,7 @@
             argumentList = buildArgumentList { arguments += args }
             source = data.treeToStack.last()!!.toFirSourceElement()
             interpolationPrefix = ""
+            fromStringLiterals = true
         }
     }
 
diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt
index 56a46c6..cc48a7f 100644
--- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt
+++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt
@@ -3146,6 +3146,7 @@
                 argumentList = buildArgumentList { arguments += args }
                 source = data.treeToStack.last()!!.toFirSourceElement()
                 interpolationPrefix = ""
+                fromStringLiterals = true
             }
         }
 
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirStringConcatenationCall.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirStringConcatenationCall.kt
index 37f836a..b1d982b 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirStringConcatenationCall.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirStringConcatenationCall.kt
@@ -24,6 +24,7 @@
     @UnresolvedExpressionTypeAccess
     abstract override val coneTypeOrNull: ConeKotlinType?
     abstract val interpolationPrefix: String
+    abstract val fromStringLiterals: Boolean
 
     override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R =
         visitor.visitStringConcatenationCall(this, data)
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirStringConcatenationCallBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirStringConcatenationCallBuilder.kt
index 873ed59..a024067 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirStringConcatenationCallBuilder.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirStringConcatenationCallBuilder.kt
@@ -27,6 +27,7 @@
     override val annotations: MutableList<FirAnnotation> = mutableListOf()
     override lateinit var argumentList: FirArgumentList
     lateinit var interpolationPrefix: String
+    var fromStringLiterals: Boolean = false
 
     override fun build(): FirStringConcatenationCall {
         return FirStringConcatenationCallImpl(
@@ -34,6 +35,7 @@
             annotations.toMutableOrEmpty(),
             argumentList,
             interpolationPrefix,
+            fromStringLiterals,
         )
     }
 
diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirStringConcatenationCallImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirStringConcatenationCallImpl.kt
index 33a345b..8b079c8 100644
--- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirStringConcatenationCallImpl.kt
+++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirStringConcatenationCallImpl.kt
@@ -30,6 +30,7 @@
     override var annotations: MutableOrEmptyList<FirAnnotation>,
     override var argumentList: FirArgumentList,
     override val interpolationPrefix: String,
+    override val fromStringLiterals: Boolean,
 ) : FirStringConcatenationCall() {
     @OptIn(UnresolvedExpressionTypeAccess::class)
     override val coneTypeOrNull: ConeKotlinType? = StandardClassIds.String.constructClassLikeType()
diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt
index 525c659..2257442 100644
--- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt
+++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt
@@ -273,6 +273,7 @@
 
         builder(stringConcatenationCall) {
             parents += callBuilder
+            defaultFalse("fromStringLiterals")
         }
 
         builder(thisReceiverExpression) {
diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt
index c2b3e1f..a985049 100644
--- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt
+++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/FirTree.kt
@@ -1020,6 +1020,7 @@
         parent(expression)
 
         +field("interpolationPrefix", string)
+        +field("fromStringLiterals", boolean)
     }
 
     val throwExpression: Element by element(Expression) {