[LL FIR] dependent analyze: support different numbers of statements
Previously, the different number of statements has led to some unexpected behavior. E.g., the resulting script can have duplicated declarations – one from the original script instead of some statement and one from the copy ^KT-60987
This commit is contained in:
committed by
Space Team
parent
4f4b30ee28
commit
b0b2e76e13
+69
-9
@@ -72,15 +72,26 @@ object LowLevelFirApiFacadeForResolveOnAir {
|
||||
fake.originalDeclaration = original
|
||||
}
|
||||
|
||||
/**
|
||||
* We assume that [targetDeclaration] should have the same number of declarations
|
||||
*
|
||||
* @see restoreOriginalDeclarationsInScript
|
||||
*/
|
||||
if (targetDeclaration is KtScript && originalDeclaration is KtScript) {
|
||||
originalDeclaration.blockExpression.statements.zip(targetDeclaration.blockExpression.statements) { original, fake ->
|
||||
if (original is KtDeclaration && fake is KtDeclaration) {
|
||||
fake.originalDeclaration = original
|
||||
}
|
||||
val originalStatements = originalDeclaration.declarationSequence()
|
||||
val newStatements = targetDeclaration.declarationSequence()
|
||||
originalStatements.zip(newStatements) { original, fake ->
|
||||
fake.originalDeclaration = original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtScript.declarationSequence(): Sequence<KtDeclaration> {
|
||||
val sequence = blockExpression.statements.asSequence().filter { it !is KtScriptInitializer && it is KtDeclaration }
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return sequence as Sequence<KtDeclaration>
|
||||
}
|
||||
|
||||
fun <T : KtElement> onAirResolveElement(
|
||||
firResolveSession: LLFirResolveSession,
|
||||
place: T,
|
||||
@@ -338,11 +349,7 @@ object LowLevelFirApiFacadeForResolveOnAir {
|
||||
* We shouldn't touch script declarations because they're independent of a script
|
||||
*/
|
||||
if (newFirDeclaration is FirScript && originalFirDeclaration is FirScript) {
|
||||
val newStatements = originalFirDeclaration.statements.zip(newFirDeclaration.statements).map { (original, copied) ->
|
||||
original.takeUnless(FirStatement::isScriptStatement) ?: copied
|
||||
}
|
||||
|
||||
newFirDeclaration.replaceStatements(newStatements)
|
||||
restoreOriginalDeclarationsInScript(originalScript = originalFirDeclaration, newScript = newFirDeclaration)
|
||||
}
|
||||
|
||||
session.moduleComponents.firModuleLazyDeclarationResolver.runLazyDesignatedOnAirResolve(
|
||||
@@ -354,6 +361,59 @@ object LowLevelFirApiFacadeForResolveOnAir {
|
||||
return newFirDeclaration
|
||||
}
|
||||
|
||||
/**
|
||||
* We assume that [newScript] has the same declarations as [originalScript]
|
||||
*/
|
||||
private fun restoreOriginalDeclarationsInScript(originalScript: FirScript, newScript: FirScript) {
|
||||
val updatedStatements = ArrayList<FirStatement>(newScript.statements.size)
|
||||
val originalDeclarations = originalScript.statements.iterator()
|
||||
for (recreatedStatement in newScript.statements) {
|
||||
updatedStatements += if (recreatedStatement.isScriptStatement) {
|
||||
recreatedStatement
|
||||
} else {
|
||||
originalDeclarations.nextDeclaration() ?: scriptDeclarationInconsistencyError(originalScript, newScript)
|
||||
}
|
||||
}
|
||||
|
||||
val nextDeclaration = originalDeclarations.nextDeclaration()
|
||||
if (nextDeclaration != null) {
|
||||
scriptDeclarationInconsistencyError(originalScript, newScript)
|
||||
}
|
||||
|
||||
newScript.replaceStatements(updatedStatements)
|
||||
}
|
||||
|
||||
private fun scriptDeclarationInconsistencyError(originalScript: FirScript, newScript: FirScript): Nothing {
|
||||
val originalDeclarations = originalScript.statements.filterNot(FirStatement::isScriptStatement)
|
||||
val newDeclarations = newScript.statements.filterNot(FirStatement::isScriptStatement)
|
||||
errorWithAttachment("New script has ${if (newDeclarations.size > originalDeclarations.size) "more" else "less"} declarations") {
|
||||
withFirEntry("originalScript", originalScript)
|
||||
withFirEntry("newScript", newScript)
|
||||
withEntryGroup("originalDeclarations") {
|
||||
for ((index, declaration) in originalDeclarations.withIndex()) {
|
||||
withFirEntry(index.toString(), declaration)
|
||||
}
|
||||
}
|
||||
|
||||
withEntryGroup("newDeclarations") {
|
||||
for ((index, declaration) in newDeclarations.withIndex()) {
|
||||
withFirEntry(index.toString(), declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Iterator<FirStatement>.nextDeclaration(): FirStatement? {
|
||||
while (hasNext()) {
|
||||
val statement = next()
|
||||
if (statement.isScriptStatement) continue
|
||||
|
||||
return statement
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun computeDesignation(originalDeclaration: KtElement, originalFirFile: FirFile): FirElementFinder.FirDeclarationDesignation? {
|
||||
if (originalDeclaration is KtCodeFragment) {
|
||||
val firCodeFragment = originalFirFile.codeFragment
|
||||
|
||||
+14
-10
@@ -16,11 +16,12 @@ Tower Data Context:
|
||||
Element 7
|
||||
Scope: FirScriptDeclarationsScope
|
||||
Functions
|
||||
FirNamedFunctionSymbol public? final? fun foo(i: Int, action: ( (Int) -> Unit )): R|kotlin/Unit|
|
||||
FirNamedFunctionSymbol public? final? fun foo(i: Int, action: ( (Int) -> Unit )): R|kotlin/Unit|
|
||||
FirNamedFunctionSymbol public? final? fun scriptFunction(): <implicit>
|
||||
FirNamedFunctionSymbol public? final? fun unusedScriptFunction(p: String): <implicit>
|
||||
FirNamedFunctionSymbol public? final? fun unusedScriptFunction(p: String): <implicit>
|
||||
FirNamedFunctionSymbol public final fun foo(i: R|kotlin/Int|, action: R|(kotlin/Int) -> kotlin/Unit|): R|kotlin/Unit|
|
||||
FirNamedFunctionSymbol public final fun scriptFunction(): R|kotlin/Int|
|
||||
FirNamedFunctionSymbol public final fun unusedScriptFunction(p: R|kotlin/String|): R|kotlin/Int|
|
||||
Properties:
|
||||
FirPropertySymbol public final val $$result: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int|
|
||||
Element 8
|
||||
Scope: FirLocalScope
|
||||
Properties:
|
||||
@@ -31,10 +32,13 @@ Tower Data Context:
|
||||
SCRIPT:
|
||||
lval args: R|kotlin/Array<kotlin/String>|
|
||||
|
||||
public? final? fun scriptFunction(): <implicit>
|
||||
public? final? fun unusedScriptFunction(p: String): <implicit>
|
||||
public? final? fun unusedScriptFunction(p: String): <implicit>
|
||||
public? final? fun foo(i: Int, action: ( (Int) -> Unit )): R|kotlin/Unit|
|
||||
public? final? fun foo(i: Int, action: ( (Int) -> Unit )): R|kotlin/Unit|
|
||||
public final fun scriptFunction(): R|kotlin/Int|
|
||||
R|/scriptFunction|()
|
||||
public final fun unusedScriptFunction(p: R|kotlin/String|): R|kotlin/Int|
|
||||
R|/scriptFunction|()
|
||||
public final fun foo(i: R|kotlin/Int|, action: R|(kotlin/Int) -> kotlin/Unit|): R|kotlin/Unit|
|
||||
R|/foo|(R|/scriptFunction|(), <L> = foo@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Unit| <inline=NoInline> )
|
||||
public final val $$result: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int|
|
||||
Type: kotlin/script/templates/standard/ScriptTemplateWithArgs
|
||||
Label: <script>
|
||||
|
||||
+15
-8
@@ -7,16 +7,23 @@ context(<script>@R|kotlin/script/templates/standard/ScriptTemplateWithArgs|)
|
||||
SCRIPT: [ResolvedTo(BODY_RESOLVE)]
|
||||
[ResolvedTo(BODY_RESOLVE)] lval args: R|kotlin/Array<kotlin/String>|
|
||||
|
||||
public? final? [ResolvedTo(RAW_FIR)] fun scriptFunction(): <implicit> { LAZY_BLOCK }
|
||||
|
||||
public? final? [ResolvedTo(RAW_FIR)] fun unusedScriptFunction([ResolvedTo(RAW_FIR)] p: String): <implicit> { LAZY_BLOCK }
|
||||
|
||||
public? final? [ResolvedTo(RAW_FIR)] fun unusedScriptFunction([ResolvedTo(RAW_FIR)] p: String): <implicit> {
|
||||
^unusedScriptFunction IntegerLiteral(22)
|
||||
public final [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] fun scriptFunction(): R|kotlin/Int| {
|
||||
^scriptFunction Int(42)
|
||||
}
|
||||
|
||||
public? final? [ResolvedTo(RAW_FIR)] fun foo([ResolvedTo(RAW_FIR)] i: Int, [ResolvedTo(RAW_FIR)] action: ( (Int) -> Unit )): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
R|/scriptFunction|()
|
||||
public final [ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] fun unusedScriptFunction([ResolvedTo(IMPLICIT_TYPES_BODY_RESOLVE)] p: R|kotlin/String|): R|kotlin/Int| {
|
||||
^unusedScriptFunction Int(22)
|
||||
}
|
||||
|
||||
public? final? [ResolvedTo(RAW_FIR)] fun foo([ResolvedTo(RAW_FIR)] i: Int, [ResolvedTo(RAW_FIR)] action: ( (Int) -> Unit )): R|kotlin/Unit| {
|
||||
R|/scriptFunction|()
|
||||
public final [ResolvedTo(CONTRACTS)] fun foo([ResolvedTo(CONTRACTS)] i: R|kotlin/Int|, [ResolvedTo(CONTRACTS)] action: R|(kotlin/Int) -> kotlin/Unit|): R|kotlin/Unit| {
|
||||
action#(i#)
|
||||
}
|
||||
|
||||
R|/foo|(R|/scriptFunction|(), <L> = [ResolvedTo(BODY_RESOLVE)] [MatchingParameterFunctionTypeKey=kotlin/Function1<kotlin/Int, kotlin/Unit>] foo@fun <anonymous>([ResolvedTo(BODY_RESOLVE)] it: R|kotlin/Int|): R|kotlin/Unit| <inline=NoInline> {
|
||||
R|/scriptFunction|()
|
||||
}
|
||||
)
|
||||
public final [ResolvedTo(BODY_RESOLVE)] val $$result: R|kotlin/Int| = R|/unusedScriptFunction|(String(str))
|
||||
public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
|
||||
Reference in New Issue
Block a user