Skip to content

Commit 8267d33

Browse files
Kotlin: converge default-accessor body spans onto the K2 signature span
A compiler-generated default property accessor (`DEFAULT_PROPERTY_ACCESSOR`) has no source body, so the two frontends anchor its synthesised body expressions (`field = value` / `return field`, plus their `<set-?>`, field and `this` sub-accesses) differently: - K1 anchors them at the whole `KtProperty`, whose end offset runs through the initialiser (`var topLevelInt: Int = 0` -> `60:1:60:24`, `var curValue = 0` -> `26:13:26:24` .. `26:28`). - K2 has no PSI for these accessors and falls back to the raw signature span, which stops at the type (`60:1:60:20`) or, when the type is inferred, at the name (`26:13:26:24`). The initialiser is not part of the accessor body (it runs in the field initialiser / `<clinit>`, not in the setter), so K2's narrower signature span is the more intuitive, information-preserving choice: it keeps the accessor's synthetic expressions on the property signature and leaves the initialiser to the genuine `KtInitializerAssignExpr` / `<clinit>` rows. We converge K1 onto the K2 span with a scoped offset remap set only while extracting the accessor body (mirroring the existing delegated-property-accessor remap). The remap is keyed off the accessor's corresponding property and matches the property's full IR range exactly, so: - it only rewrites the synthetic body expressions that carry that range; - the real field-initialiser rows (which share the same source text but are extracted in the initialiser context) are untouched and keep their initialiser-inclusive spans; and - it is a no-op under K2 (no PSI, so `getEnclosingKtProperty` returns null) and for any property without an initialiser past its signature. Tradeoff: this converges K1 onto a span that K2 produces natively but that K1 cannot recover without the PSI, so the direction is fixed (K1 -> K2) rather than chosen freely. That is acceptable here because the K2 span is the more correct one on the merits (the initialiser does not belong to the accessor body). Expected updates (K1 only; K2 already emitted these): - library-tests/exprs/exprs.expected (setCurValue, setTopLevelInt) - library-tests/methods/exprs.expected (clinit.kt setTopLevelInt) Both suites relearned; all tests pass. Divergence 820 -> 798 rows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 86bfc022-3ecc-4746-ba23-3b76c4e4c3e4
1 parent fe38154 commit 8267d33

3 files changed

Lines changed: 53 additions & 14 deletions

File tree

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2580,9 +2580,13 @@ open class KotlinFileExtractor(
25802580
f
25812581
)
25822582
val remap =
2583-
if (f.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR)
2584-
getDelegateExpressionOffsetRemap(f)
2585-
else null
2583+
when (f.origin) {
2584+
IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR ->
2585+
getDelegateExpressionOffsetRemap(f)
2586+
IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR ->
2587+
getDefaultAccessorBodyOffsetRemap(f)
2588+
else -> null
2589+
}
25862590
val previousRemap = tw.scopedOffsetRemap
25872591
if (remap != null) tw.scopedOffsetRemap = remap
25882592
try {
@@ -3581,6 +3585,41 @@ open class KotlinFileExtractor(
35813585
)
35823586
}
35833587

3588+
/**
3589+
* Maps the property-declaration range (which the K1 frontend records for the synthesised
3590+
* body expressions of a compiler-generated default `get`/`set` accessor, running through the
3591+
* property initialiser) onto the property *signature* range (the `val`/`var` keyword through
3592+
* the type, or through the name when the type is inferred), which K2 records natively.
3593+
*
3594+
* A `DEFAULT_PROPERTY_ACCESSOR` has no source body, so the K1 frontend anchors its synthesised
3595+
* `field = value` / `return field` expressions (and their `<set-?>`/field/`this` sub-accesses)
3596+
* at the whole [KtProperty], whose end offset runs through the initialiser
3597+
* (`var topLevelInt: Int = 0` -> `60:1:60:24`). K2 has no PSI for these accessors and falls
3598+
* back to the raw signature span, which stops at the type (`60:1:60:20`) or, when the type is
3599+
* inferred, at the name (`var curValue = 0` -> `26:13:26:24`). The initialiser is not part of
3600+
* the accessor body, so K2's narrower signature span is the more intuitive one; we converge K1
3601+
* onto it via a scoped offset remap set only while extracting the accessor body. The remap
3602+
* matches the property range exactly, so no unrelated location is affected.
3603+
*
3604+
* Returns null under K2 (no PSI, [getEnclosingKtProperty] returns null) and when the property
3605+
* has no initialiser past the signature to exclude.
3606+
*/
3607+
private fun getDefaultAccessorBodyOffsetRemap(
3608+
f: IrFunction
3609+
): Pair<Pair<Int, Int>, Pair<Int, Int>>? {
3610+
val property = (f as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return null
3611+
if (property.startOffset < 0 || property.endOffset < 0) return null
3612+
val ktProperty = getEnclosingKtProperty(property) ?: return null
3613+
val declEnd = ktProperty.typeReference?.endOffset
3614+
?: ktProperty.nameIdentifier?.endOffset
3615+
?: return null
3616+
if (property.endOffset <= declEnd) return null
3617+
return Pair(
3618+
Pair(property.startOffset, property.endOffset),
3619+
Pair(property.startOffset, declEnd)
3620+
)
3621+
}
3622+
35843623
/**
35853624
* Returns the [KtProperty] enclosing the PSI element that back-maps from IR element [e],
35863625
* or null when no PSI is available (as under K2, where [getKtFile]/[findPsiElement] return

java/ql/test-kotlin1/library-tests/exprs/exprs.expected

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,18 @@
116116
| delegatedProperties.kt:25:64:31:9 | <Stmt> | delegatedProperties.kt:25:9:31:9 | resourceDelegate | StmtExpr |
117117
| delegatedProperties.kt:25:64:31:9 | ReadWriteProperty<Object,Integer> | delegatedProperties.kt:25:9:31:9 | resourceDelegate | TypeAccess |
118118
| delegatedProperties.kt:25:64:31:9 | new (...) | delegatedProperties.kt:25:9:31:9 | resourceDelegate | ClassInstanceExpr |
119+
| delegatedProperties.kt:26:13:26:24 | ...=... | delegatedProperties.kt:26:13:26:24 | setCurValue | AssignExpr |
120+
| delegatedProperties.kt:26:13:26:24 | <set-?> | delegatedProperties.kt:26:13:26:24 | setCurValue | VarAccess |
119121
| delegatedProperties.kt:26:13:26:24 | Unit | file://:0:0:0:0 | <none> | TypeAccess |
120122
| delegatedProperties.kt:26:13:26:24 | int | file://:0:0:0:0 | <none> | TypeAccess |
121123
| delegatedProperties.kt:26:13:26:24 | int | file://:0:0:0:0 | <none> | TypeAccess |
122124
| delegatedProperties.kt:26:13:26:24 | this | delegatedProperties.kt:26:13:26:24 | getCurValue | ThisAccess |
123125
| delegatedProperties.kt:26:13:26:24 | this | delegatedProperties.kt:26:13:26:24 | setCurValue | ThisAccess |
124126
| delegatedProperties.kt:26:13:26:24 | this.curValue | delegatedProperties.kt:26:13:26:24 | getCurValue | VarAccess |
127+
| delegatedProperties.kt:26:13:26:24 | this.curValue | delegatedProperties.kt:26:13:26:24 | setCurValue | VarAccess |
125128
| delegatedProperties.kt:26:13:26:28 | ...=... | delegatedProperties.kt:25:64:31:9 | | KtInitializerAssignExpr |
126-
| delegatedProperties.kt:26:13:26:28 | ...=... | delegatedProperties.kt:26:13:26:24 | setCurValue | AssignExpr |
127-
| delegatedProperties.kt:26:13:26:28 | <set-?> | delegatedProperties.kt:26:13:26:24 | setCurValue | VarAccess |
128129
| delegatedProperties.kt:26:13:26:28 | curValue | delegatedProperties.kt:25:64:31:9 | | VarAccess |
129130
| delegatedProperties.kt:26:13:26:28 | int | file://:0:0:0:0 | <none> | TypeAccess |
130-
| delegatedProperties.kt:26:13:26:28 | this.curValue | delegatedProperties.kt:26:13:26:24 | setCurValue | VarAccess |
131131
| delegatedProperties.kt:26:28:26:28 | 0 | delegatedProperties.kt:25:64:31:9 | | IntegerLiteral |
132132
| delegatedProperties.kt:27:22:27:88 | int | file://:0:0:0:0 | <none> | TypeAccess |
133133
| delegatedProperties.kt:27:35:27:47 | Object | file://:0:0:0:0 | <none> | TypeAccess |
@@ -281,18 +281,18 @@
281281
| delegatedProperties.kt:54:51:54:68 | KProperty<?> | file://:0:0:0:0 | <none> | TypeAccess |
282282
| delegatedProperties.kt:56:16:56:33 | ResourceDelegate | delegatedProperties.kt:54:14:57:5 | provideDelegate | TypeAccess |
283283
| delegatedProperties.kt:56:16:56:33 | new ResourceDelegate(...) | delegatedProperties.kt:54:14:57:5 | provideDelegate | ClassInstanceExpr |
284+
| delegatedProperties.kt:60:1:60:20 | ...=... | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | AssignExpr |
285+
| delegatedProperties.kt:60:1:60:20 | <set-?> | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | VarAccess |
284286
| delegatedProperties.kt:60:1:60:20 | DelegatedPropertiesKt | delegatedProperties.kt:60:1:60:20 | getTopLevelInt | TypeAccess |
287+
| delegatedProperties.kt:60:1:60:20 | DelegatedPropertiesKt | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | TypeAccess |
285288
| delegatedProperties.kt:60:1:60:20 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:60:1:60:20 | getTopLevelInt | VarAccess |
289+
| delegatedProperties.kt:60:1:60:20 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | VarAccess |
286290
| delegatedProperties.kt:60:1:60:20 | Unit | file://:0:0:0:0 | <none> | TypeAccess |
287291
| delegatedProperties.kt:60:1:60:20 | int | file://:0:0:0:0 | <none> | TypeAccess |
288292
| delegatedProperties.kt:60:1:60:20 | int | file://:0:0:0:0 | <none> | TypeAccess |
289293
| delegatedProperties.kt:60:1:60:24 | ...=... | delegatedProperties.kt:0:0:0:0 | <clinit> | KtInitializerAssignExpr |
290-
| delegatedProperties.kt:60:1:60:24 | ...=... | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | AssignExpr |
291-
| delegatedProperties.kt:60:1:60:24 | <set-?> | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | VarAccess |
292294
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt | delegatedProperties.kt:0:0:0:0 | <clinit> | TypeAccess |
293-
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | TypeAccess |
294295
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:0:0:0:0 | <clinit> | VarAccess |
295-
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | VarAccess |
296296
| delegatedProperties.kt:60:1:60:24 | int | file://:0:0:0:0 | <none> | TypeAccess |
297297
| delegatedProperties.kt:60:24:60:24 | 0 | delegatedProperties.kt:0:0:0:0 | <clinit> | IntegerLiteral |
298298
| delegatedProperties.kt:62:25:62:48 | ...=... | delegatedProperties.kt:62:24:62:49 | ClassWithDelegate | KtInitializerAssignExpr |

java/ql/test-kotlin1/library-tests/methods/exprs.expected

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
| clinit.kt:3:1:3:20 | ...=... | AssignExpr |
2+
| clinit.kt:3:1:3:20 | <set-?> | VarAccess |
13
| clinit.kt:3:1:3:20 | ClinitKt | TypeAccess |
4+
| clinit.kt:3:1:3:20 | ClinitKt | TypeAccess |
5+
| clinit.kt:3:1:3:20 | ClinitKt.topLevelInt | VarAccess |
26
| clinit.kt:3:1:3:20 | ClinitKt.topLevelInt | VarAccess |
37
| clinit.kt:3:1:3:20 | Unit | TypeAccess |
48
| clinit.kt:3:1:3:20 | int | TypeAccess |
59
| clinit.kt:3:1:3:20 | int | TypeAccess |
6-
| clinit.kt:3:1:3:24 | ...=... | AssignExpr |
710
| clinit.kt:3:1:3:24 | ...=... | KtInitializerAssignExpr |
8-
| clinit.kt:3:1:3:24 | <set-?> | VarAccess |
911
| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess |
10-
| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess |
11-
| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess |
1212
| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess |
1313
| clinit.kt:3:1:3:24 | int | TypeAccess |
1414
| clinit.kt:3:24:3:24 | 0 | IntegerLiteral |

0 commit comments

Comments
 (0)