Kotlin: converge synthetic property-access locations to the K2 signature span

For a property such as `val prop: Int = 1`, the compiler generates synthetic
`this.prop` accesses (inside the default getter and the primary-constructor field
initialiser). The K1 and K2 frontends give these synthetic accesses different
source spans:

  * K1 runs the span through the initialiser: `variables.kt:3:5:3:21`
  * K2 stops at the end of the type signature:  `variables.kt:3:5:3:17`

The property *declaration* location is `3:21` under both frontends and is left
untouched; only these synthetic *access* expressions diverged. The K2 signature
span (val/var keyword to the end of the type, or to the end of the name when the
type is inferred) is the more intuitive location: it points at the property's
signature rather than incidentally swallowing the initialiser expression, so a
`this.prop` read is not reported as spanning code it does not evaluate.

We adopt the K2 span and reproduce it under K1 from PSI. New helper
`getPsiBasedPropertySignatureAccessLocation` returns the signature span only when
the access resolves (via `findPsiElement`) to the enclosing `KtProperty`, which is
true exactly for these synthetic property-declaration-anchored accesses. It returns
null under K2 (no PSI is available there, and the raw IR offset already gives the
signature span) and for every ordinary source-written access (whose PSI is the
reference expression, not the whole declaration), so canonical K2 output and normal
accesses are unaffected. It is wired into the `IrGetField` read path and into
`extractThisAccess`.

Effect: tk1 `variables/variables.expected` becomes byte-identical to tk2;
`variables/variableAccesses.expected` drops to only the separate implicit-`this`
call-receiver span divergence; genericExprTypes/exprs/methods synthetic
property-access rows all move from the through-initialiser span onto the K2
signature span. All 3333 tests pass in both suites; test-kotlin2 is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Anders Fugmann
2026-07-13 17:50:12 +02:00
parent f1ccb6bda0
commit d91b3899ae
6 changed files with 49 additions and 23 deletions

View File

@@ -3099,6 +3099,31 @@ open class KotlinFileExtractor(
return tw.getLocation(declStart, declEnd)
}
/**
* When a *synthetic* property access (e.g. the `this.prop` read the compiler generates
* inside a default getter or a primary-constructor field initialiser) carries IR offsets
* that span the whole property declaration, returns the property *signature* span (the
* `val`/`var` keyword to the end of the type, or to the end of the name when the type is
* inferred), matching the location the K2 frontend records for the same synthetic access;
* otherwise returns null.
*
* K1 and K2 offset these synthetic accesses differently: K1 runs through the initialiser
* (`val prop: Int = 1` -> `3:5:3:21`) while K2 stops at the type (`3:5:3:17`). Under K1 the
* access resolves (via [findPsiElement]) to the enclosing [KtProperty], from which we recover
* the K2 signature span. Returns null under K2 (no PSI; the raw offset already gives the
* signature span) and for every ordinary, source-written access, whose PSI is the reference
* expression rather than the whole property declaration.
*/
private fun getPsiBasedPropertySignatureAccessLocation(e: IrElement): Label<DbLocation>? {
val file = currentIrFile ?: return null
val psi2Ir = getPsi2Ir() ?: return null
val ktProperty = psi2Ir.findPsiElement(e, file) as? KtProperty ?: return null
val declStart = ktProperty.valOrVarKeyword.startOffset
val declEnd = (ktProperty.typeReference ?: ktProperty.nameIdentifier)?.endOffset
?: return null
return tw.getLocation(declStart, declEnd)
}
// Matches the K1 frontend's synthetic name for the temporary holding the subject of a
// destructuring declaration (`val (a, b) = subject`), which is `tmp<N>_container`. K2 names
// the same temporary with the special name `<destruct>`.
@@ -6975,7 +7000,7 @@ open class KotlinFileExtractor(
is IrGetField -> {
val exprParent = parent.expr(e, callable)
val owner = tryReplaceAndroidSyntheticField(e.symbol.owner)
val locId = tw.getLocation(e)
val locId = getPsiBasedPropertySignatureAccessLocation(e) ?: tw.getLocation(e)
val fieldType =
if (isAnnotationClassField(owner)) kClassToJavaClass(e.type) else e.type
extractVariableAccess(
@@ -7543,6 +7568,7 @@ open class KotlinFileExtractor(
) {
val containingDeclaration = declarationStack.peek().first
val locId = getDelegatedAccessorSyntheticArgumentLocation(e)
?: getPsiBasedPropertySignatureAccessLocation(e)
?: getPsiBasedLocation(e) ?: tw.getLocation(e)
if (

View File

@@ -7,22 +7,22 @@
| generic_anonymous.kt:1:26:1:33 | this | Generic |
| generic_anonymous.kt:1:26:1:33 | this.t | T |
| generic_anonymous.kt:3:3:5:3 | ...=... | new Object(...) { ... } |
| generic_anonymous.kt:3:3:5:3 | this | Generic |
| generic_anonymous.kt:3:3:5:3 | x | new Object(...) { ... } |
| generic_anonymous.kt:3:11:3:15 | T | T |
| generic_anonymous.kt:3:11:3:15 | new Object(...) { ... } | new Object(...) { ... } |
| generic_anonymous.kt:3:11:3:15 | this | Generic |
| generic_anonymous.kt:3:11:3:15 | this.x | new Object(...) { ... } |
| generic_anonymous.kt:3:11:5:3 | T | T |
| generic_anonymous.kt:3:11:5:3 | new Object(...) { ... } | new Object(...) { ... } |
| generic_anonymous.kt:3:11:5:3 | this.x | new Object(...) { ... } |
| generic_anonymous.kt:3:19:5:3 | <Stmt> | new Object(...) { ... } |
| generic_anonymous.kt:3:19:5:3 | Object | Object |
| generic_anonymous.kt:3:19:5:3 | new (...) | new Object(...) { ... } |
| generic_anonymous.kt:4:7:4:16 | T | T |
| generic_anonymous.kt:4:7:4:16 | this | new Object(...) { ... } |
| generic_anonymous.kt:4:7:4:16 | this.member | T |
| generic_anonymous.kt:4:7:4:20 | ...=... | T |
| generic_anonymous.kt:4:7:4:20 | T | T |
| generic_anonymous.kt:4:7:4:20 | member | T |
| generic_anonymous.kt:4:7:4:20 | this | new Object(...) { ... } |
| generic_anonymous.kt:4:7:4:20 | this.member | T |
| generic_anonymous.kt:4:20:4:20 | Generic | Generic |
| generic_anonymous.kt:4:20:4:20 | Generic.this | Generic |
| generic_anonymous.kt:4:20:4:20 | getT(...) | T |

View File

@@ -119,14 +119,14 @@
| delegatedProperties.kt:26:13:26:24 | Unit | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:26:13:26:24 | int | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:26:13:26:24 | int | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:26:13:26:24 | this | delegatedProperties.kt:26:13:26:24 | getCurValue | ThisAccess |
| delegatedProperties.kt:26:13:26:24 | this | delegatedProperties.kt:26:13:26:24 | setCurValue | ThisAccess |
| delegatedProperties.kt:26:13:26:24 | this.curValue | delegatedProperties.kt:26:13:26:24 | getCurValue | VarAccess |
| delegatedProperties.kt:26:13:26:28 | ...=... | delegatedProperties.kt:25:64:31:9 | | KtInitializerAssignExpr |
| delegatedProperties.kt:26:13:26:28 | ...=... | delegatedProperties.kt:26:13:26:24 | setCurValue | AssignExpr |
| delegatedProperties.kt:26:13:26:28 | <set-?> | delegatedProperties.kt:26:13:26:24 | setCurValue | VarAccess |
| delegatedProperties.kt:26:13:26:28 | curValue | delegatedProperties.kt:25:64:31:9 | | VarAccess |
| delegatedProperties.kt:26:13:26:28 | int | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:26:13:26:28 | this | delegatedProperties.kt:26:13:26:24 | getCurValue | ThisAccess |
| delegatedProperties.kt:26:13:26:28 | this | delegatedProperties.kt:26:13:26:24 | setCurValue | ThisAccess |
| delegatedProperties.kt:26:13:26:28 | this.curValue | delegatedProperties.kt:26:13:26:24 | getCurValue | VarAccess |
| delegatedProperties.kt:26:13:26:28 | this.curValue | delegatedProperties.kt:26:13:26:24 | setCurValue | VarAccess |
| delegatedProperties.kt:26:28:26:28 | 0 | delegatedProperties.kt:25:64:31:9 | | IntegerLiteral |
| delegatedProperties.kt:27:22:27:88 | int | file://:0:0:0:0 | <none> | TypeAccess |
@@ -281,6 +281,8 @@
| delegatedProperties.kt:54:51:54:68 | KProperty<?> | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:56:16:56:33 | ResourceDelegate | delegatedProperties.kt:54:14:57:5 | provideDelegate | TypeAccess |
| delegatedProperties.kt:56:16:56:33 | new ResourceDelegate(...) | delegatedProperties.kt:54:14:57:5 | provideDelegate | ClassInstanceExpr |
| delegatedProperties.kt:60:1:60:20 | DelegatedPropertiesKt | delegatedProperties.kt:60:1:60:20 | getTopLevelInt | TypeAccess |
| delegatedProperties.kt:60:1:60:20 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:60:1:60:20 | getTopLevelInt | VarAccess |
| delegatedProperties.kt:60:1:60:20 | Unit | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:60:1:60:20 | int | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:60:1:60:20 | int | file://:0:0:0:0 | <none> | TypeAccess |
@@ -288,10 +290,8 @@
| delegatedProperties.kt:60:1:60:24 | ...=... | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | AssignExpr |
| delegatedProperties.kt:60:1:60:24 | <set-?> | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | VarAccess |
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt | delegatedProperties.kt:0:0:0:0 | <clinit> | TypeAccess |
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt | delegatedProperties.kt:60:1:60:20 | getTopLevelInt | TypeAccess |
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | TypeAccess |
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:0:0:0:0 | <clinit> | VarAccess |
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:60:1:60:20 | getTopLevelInt | VarAccess |
| delegatedProperties.kt:60:1:60:24 | DelegatedPropertiesKt.topLevelInt | delegatedProperties.kt:60:1:60:20 | setTopLevelInt | VarAccess |
| delegatedProperties.kt:60:1:60:24 | int | file://:0:0:0:0 | <none> | TypeAccess |
| delegatedProperties.kt:60:24:60:24 | 0 | delegatedProperties.kt:0:0:0:0 | <clinit> | IntegerLiteral |
@@ -1545,11 +1545,11 @@
| exprs.kt:186:23:186:27 | Color | exprs.kt:184:1:187:1 | enums | TypeAccess |
| exprs.kt:186:23:186:27 | Color.GREEN | exprs.kt:184:1:187:1 | enums | VarAccess |
| exprs.kt:192:5:192:10 | int | file://:0:0:0:0 | <none> | TypeAccess |
| exprs.kt:192:5:192:10 | this | exprs.kt:192:5:192:10 | getA1 | ThisAccess |
| exprs.kt:192:5:192:10 | this.a1 | exprs.kt:192:5:192:10 | getA1 | VarAccess |
| exprs.kt:192:5:192:14 | ...=... | exprs.kt:191:1:199:1 | Class1 | KtInitializerAssignExpr |
| exprs.kt:192:5:192:14 | a1 | exprs.kt:191:1:199:1 | Class1 | VarAccess |
| exprs.kt:192:5:192:14 | int | file://:0:0:0:0 | <none> | TypeAccess |
| exprs.kt:192:5:192:14 | this | exprs.kt:192:5:192:10 | getA1 | ThisAccess |
| exprs.kt:192:5:192:14 | this.a1 | exprs.kt:192:5:192:10 | getA1 | VarAccess |
| exprs.kt:192:14:192:14 | 1 | exprs.kt:191:1:199:1 | Class1 | IntegerLiteral |
| exprs.kt:193:13:198:5 | Object | file://:0:0:0:0 | <none> | TypeAccess |
| exprs.kt:194:9:194:18 | a2 | exprs.kt:193:13:198:5 | getObject | LocalVariableDeclExpr |
@@ -1558,11 +1558,11 @@
| exprs.kt:195:16:197:9 | Interface1 | exprs.kt:193:13:198:5 | getObject | TypeAccess |
| exprs.kt:195:16:197:9 | new (...) | exprs.kt:193:13:198:5 | getObject | ClassInstanceExpr |
| exprs.kt:196:13:196:26 | String | file://:0:0:0:0 | <none> | TypeAccess |
| exprs.kt:196:13:196:26 | this | exprs.kt:196:13:196:26 | getA3 | ThisAccess |
| exprs.kt:196:13:196:26 | this.a3 | exprs.kt:196:13:196:26 | getA3 | VarAccess |
| exprs.kt:196:13:196:49 | ...=... | exprs.kt:195:16:197:9 | | KtInitializerAssignExpr |
| exprs.kt:196:13:196:49 | String | file://:0:0:0:0 | <none> | TypeAccess |
| exprs.kt:196:13:196:49 | a3 | exprs.kt:195:16:197:9 | | VarAccess |
| exprs.kt:196:13:196:49 | this | exprs.kt:196:13:196:26 | getA3 | ThisAccess |
| exprs.kt:196:13:196:49 | this.a3 | exprs.kt:196:13:196:26 | getA3 | VarAccess |
| exprs.kt:196:31:196:32 | getA1(...) | exprs.kt:195:16:197:9 | | MethodCall |
| exprs.kt:196:31:196:32 | this | exprs.kt:195:16:197:9 | | ThisAccess |
| exprs.kt:196:31:196:37 | ... + ... | exprs.kt:195:16:197:9 | | AddExpr |
@@ -4295,10 +4295,10 @@
| samConversion.kt:59:12:59:12 | 1 | samConversion.kt:57:9:60:1 | test | IntegerLiteral |
| samConversion.kt:59:14:59:14 | 2 | samConversion.kt:57:9:60:1 | test | IntegerLiteral |
| samConversion.kt:63:5:63:9 | int | file://:0:0:0:0 | <none> | TypeAccess |
| samConversion.kt:63:5:63:9 | this | samConversion.kt:63:5:63:9 | getX | ThisAccess |
| samConversion.kt:63:5:63:9 | this.x | samConversion.kt:63:5:63:9 | getX | VarAccess |
| samConversion.kt:63:5:63:13 | ...=... | samConversion.kt:62:1:64:1 | PropertyRefsTest | KtInitializerAssignExpr |
| samConversion.kt:63:5:63:13 | int | file://:0:0:0:0 | <none> | TypeAccess |
| samConversion.kt:63:5:63:13 | this | samConversion.kt:63:5:63:9 | getX | ThisAccess |
| samConversion.kt:63:5:63:13 | this.x | samConversion.kt:63:5:63:9 | getX | VarAccess |
| samConversion.kt:63:5:63:13 | x | samConversion.kt:62:1:64:1 | PropertyRefsTest | VarAccess |
| samConversion.kt:63:13:63:13 | 1 | samConversion.kt:62:1:64:1 | PropertyRefsTest | IntegerLiteral |
| samConversion.kt:67:5:67:37 | int | file://:0:0:0:0 | <none> | TypeAccess |

View File

@@ -1,3 +1,5 @@
| clinit.kt:3:1:3:20 | ClinitKt | TypeAccess |
| clinit.kt:3:1:3:20 | ClinitKt.topLevelInt | VarAccess |
| clinit.kt:3:1:3:20 | Unit | TypeAccess |
| clinit.kt:3:1:3:20 | int | TypeAccess |
| clinit.kt:3:1:3:20 | int | TypeAccess |
@@ -6,8 +8,6 @@
| clinit.kt:3:1:3:24 | <set-?> | VarAccess |
| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess |
| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess |
| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess |
| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess |
| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess |
| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess |
| clinit.kt:3:1:3:24 | int | TypeAccess |

View File

@@ -1,11 +1,11 @@
varAcc
| variables.kt:3:5:3:17 | this.prop |
| variables.kt:3:5:3:21 | prop |
| variables.kt:3:5:3:21 | this.prop |
| variables.kt:7:17:7:22 | local1 |
| variables.kt:9:17:9:22 | local2 |
| variables.kt:10:22:10:26 | param |
| variables.kt:11:17:11:22 | local3 |
| variables.kt:15:1:15:21 | VariablesKt.topLevel |
| variables.kt:15:1:15:17 | VariablesKt.topLevel |
| variables.kt:15:1:15:21 | VariablesKt.topLevel |
| variables.kt:21:11:21:18 | o |
| variables.kt:21:11:21:18 | o |
@@ -20,7 +20,7 @@ extensionReceiverAcc
| variables.kt:32:9:32:12 | this |
| variables.kt:33:9:33:12 | this |
instAcc
| variables.kt:3:5:3:21 | this |
| variables.kt:3:5:3:17 | this |
| variables.kt:21:11:21:18 | this |
| variables.kt:26:9:26:9 | this |
| variables.kt:26:11:26:15 | this |

View File

@@ -13,8 +13,8 @@ isFinal
| variables.kt:8:9:8:26 | int local2 | non-final |
| variables.kt:10:9:10:26 | int local3 | final |
compileTimeConstant
| variables.kt:3:5:3:17 | this.prop |
| variables.kt:3:5:3:21 | prop |
| variables.kt:3:5:3:21 | this.prop |
| variables.kt:7:17:7:22 | local1 |
| variables.kt:15:1:15:21 | VariablesKt.topLevel |
| variables.kt:15:1:15:17 | VariablesKt.topLevel |
| variables.kt:15:1:15:21 | VariablesKt.topLevel |