[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) {