Skip to content

Commit 7ea8558

Browse files
Kotlin: converge delegated-property accessor synthetic thisRef locations onto K2
For `val x by Delegate()`, the compiler synthesises getter/setter bodies that forward to the delegate's `getValue`/`setValue`, passing a synthetic receiver argument (the enclosing `this`, or `null` for top-level/extension delegates). Under the K1 frontend (test-kotlin1, `-language-version 1.9`) these synthetic receiver arguments were given a bogus location `1:9:1:12`: their IR offsets (8..11) are not real source offsets for the argument, and `findPsiElement` resolves them to whatever sits at file offset 8, which for these tests is the line-1 `import`. This is meaningless and makes the receivers un-searchable by location. The K2 frontend (test-kotlin2, default) already produces the intuitive result: the synthetic member receiver is located at the DELEGATE EXPRESSION (e.g. `by ResourceDelegate()` -> `ResourceDelegate()`), and a receiver-less `null` argument gets the whole-file location `0:0:0:0`. K2 output is the canonical target for this unification (it is also compiler-version-independent). This change adds `getDelegatedAccessorSyntheticArgumentLocation`, gated on: - the enclosing declaration being an IrFunction with origin DELEGATED_PROPERTY_ACCESSOR (member/top-level accessors only), - the element being either the accessor's dispatch/extension receiver `IrGetValue` or a null `CodeQLIrConst`, and - the element offsets lying outside the enclosing `KtProperty` text range (so genuine in-source expressions are never rehomed). It returns the delegate expression's location for the `this`/receiver case and the whole-file location for the `null` case, mirroring K2. It is wired at the three sites that extract these synthetic args (extractThisAccess, the IrGetValue variable/extension-receiver path, and the null case of extractConstant). The helper relies on PSI (getPsi2Ir), so it is a no-op under K2: test-kotlin2 output is unchanged (verified). Only test-kotlin1 converges. Trade-offs / scope: - LOCAL delegated properties (declared inside a function body) are a distinct mechanism: their get/set is inlined into the enclosing function (no DELEGATED_PROPERTY_ACCESSOR origin) and the residual divergence there is an end-offset difference, not the `1:9:1:12` bug. They are deliberately left for a separate commit. - The `$delegate` backing-variable initializer/type-access spans still differ between suites (e.g. `4:18` vs `4:21`); that is an unrelated mechanism and is not touched here. - The outside-KtProperty-range gate could in principle false-negative for a delegated property declared at the very top of a file (offsets <= 11); this is preferred to over-correcting real expressions and is not observed in the test corpus. Relearned test-kotlin1 expected updated (exprs, methods; incl. PrintAst). Verified via full dual-suite relearn (CI-faithful: 2.3.20/lang-1.9 for tk1, default/2.4.0 for tk2, database consistency checks): all 3333 tests pass, only the three delegates-related tk1 files change, every changed row is a pure relocation of a synthetic receiver from `1:9:1:12` to the K2 target, and test-kotlin2 is byte-for-byte unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0de7f77 commit 7ea8558

4 files changed

Lines changed: 108 additions & 42 deletions

File tree

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

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,6 +3123,70 @@ open class KotlinFileExtractor(
31233123
return tw.getLocation(ktProperty.valOrVarKeyword.startOffset, ktProperty.endOffset)
31243124
}
31253125

3126+
/**
3127+
* For a synthetic `thisRef` argument in a compiler-generated *delegated*-property accessor
3128+
* body (origin `DELEGATED_PROPERTY_ACCESSOR`), returns the location to use, or null to leave
3129+
* the raw location in place.
3130+
*
3131+
* The synthesised getter/setter body forwards to the delegate's `getValue`/`setValue`,
3132+
* passing the accessor's dispatch/extension receiver (a `this` access) as the `thisRef`
3133+
* argument, or `null` when the property has no receiver (e.g. a local delegated property).
3134+
* These arguments have no dedicated source token. The K2 frontend records them so that:
3135+
* - a receiver `this` access takes the delegate *expression*'s source range (e.g. the
3136+
* `ResourceDelegate()` in `by ResourceDelegate()`), and
3137+
* - a `null` argument has undefined offsets, so `tw.getLocation` maps it to the whole-file
3138+
* location (`file:0:0:0:0`).
3139+
*
3140+
* The K1 frontend instead gives both real-but-meaningless offsets that resolve (via
3141+
* `findPsiElement`) to an unrelated token near the top of the file (e.g. an `import`),
3142+
* producing a bogus non-zero location. Recognise these arguments by their enclosing accessor's
3143+
* origin, their shape (the accessor's own dispatch/extension receiver, or a `null` constant),
3144+
* and the fact that their offsets fall outside the enclosing property's PSI text range (a real
3145+
* receiver access could only occur within the delegate expression, i.e. inside that range).
3146+
* Emit the K2-native location so K1 converges: the delegate expression's range for a `this`
3147+
* receiver, or the whole-file location for `null`.
3148+
*
3149+
* This is not gated on [usesK2]: it must fire under K1 (which retains PSI and produces the
3150+
* bogus offsets) and is a no-op under K2 (where [getPsi2Ir] is null, so it returns null and
3151+
* the raw location is already used).
3152+
*/
3153+
private fun getDelegatedAccessorSyntheticArgumentLocation(e: IrElement): Label<DbLocation>? {
3154+
val enclosing = declarationStack.peek().first as? IrFunction ?: return null
3155+
if (enclosing.origin != IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR) return null
3156+
val isNull =
3157+
when (e) {
3158+
is IrGetValue -> {
3159+
val owner = e.symbol.owner
3160+
val isReceiver =
3161+
owner is IrValueParameter &&
3162+
owner.parent == enclosing &&
3163+
(isDispatchReceiver(owner) ||
3164+
owner == enclosing.codeQlExtensionReceiverParameter)
3165+
if (!isReceiver) return null
3166+
false
3167+
}
3168+
is CodeQLIrConst<*> -> {
3169+
if (e.value != null) return null
3170+
true
3171+
}
3172+
else -> return null
3173+
}
3174+
val file = currentIrFile ?: return null
3175+
val psiElement = getPsi2Ir()?.findPsiElement(enclosing, file) ?: return null
3176+
val ktProperty = generateSequence(psiElement) { it.parent }
3177+
.filterIsInstance<KtProperty>().firstOrNull()
3178+
?: return null
3179+
val start = e.startOffset
3180+
val end = e.endOffset
3181+
if (start < 0 || end < 0) return null
3182+
// In-range offsets could be a genuine receiver access within the delegate expression;
3183+
// only treat offsets outside the whole property declaration as the synthetic thisRef.
3184+
if (start >= ktProperty.startOffset && end <= ktProperty.endOffset) return null
3185+
if (isNull) return tw.getWholeFileLocation()
3186+
val delegateExpression = ktProperty.delegate?.expression ?: return null
3187+
return tw.getLocation(delegateExpression.startOffset, delegateExpression.endOffset)
3188+
}
3189+
31263190
/**
31273191
* For a delegated-property accessor (origin `DELEGATED_PROPERTY_ACCESSOR`), returns the
31283192
* offset remapping to apply while extracting its body, or null if it cannot be computed.
@@ -6461,7 +6525,8 @@ open class KotlinFileExtractor(
64616525
extractVariableAccess(
64626526
useValueDeclaration(owner),
64636527
extractType,
6464-
getPsiBasedLocation(e) ?: tw.getLocation(e),
6528+
getDelegatedAccessorSyntheticArgumentLocation(e)
6529+
?: getPsiBasedLocation(e) ?: tw.getLocation(e),
64656530
exprParent.parent,
64666531
exprParent.idx,
64676532
callable,
@@ -7012,7 +7077,8 @@ open class KotlinFileExtractor(
70127077
callable: Label<out DbCallable>
70137078
) {
70147079
val containingDeclaration = declarationStack.peek().first
7015-
val locId = getPsiBasedLocation(e) ?: tw.getLocation(e)
7080+
val locId = getDelegatedAccessorSyntheticArgumentLocation(e)
7081+
?: getPsiBasedLocation(e) ?: tw.getLocation(e)
70167082

70177083
if (
70187084
containingDeclaration.shouldExtractAsStatic &&
@@ -7389,7 +7455,7 @@ open class KotlinFileExtractor(
73897455
v == null -> {
73907456
extractNull(
73917457
e.type,
7392-
tw.getLocation(e),
7458+
getDelegatedAccessorSyntheticArgumentLocation(e) ?: tw.getLocation(e),
73937459
parent,
73947460
idx,
73957461
enclosingCallable,

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ delegatedProperties.kt:
3333
# 87| -1: [TypeAccess] PropertyReferenceDelegatesKt
3434
# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegateMyClass
3535
# 87| -1: [TypeAccess] DelegatedPropertiesKt
36-
# 1| 1: [ExtensionReceiverAccess] this
36+
# 87| 1: [ExtensionReceiverAccess] this
3737
# 87| 2: [PropertyRefExpr] ...::...
3838
# 87| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
3939
# 87| 1: [Constructor]
@@ -82,7 +82,7 @@ delegatedProperties.kt:
8282
# 87| -1: [TypeAccess] PropertyReferenceDelegatesKt
8383
# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegateMyClass
8484
# 87| -1: [TypeAccess] DelegatedPropertiesKt
85-
# 1| 1: [ExtensionReceiverAccess] this
85+
# 87| 1: [ExtensionReceiverAccess] this
8686
# 87| 2: [PropertyRefExpr] ...::...
8787
# 87| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
8888
# 87| 1: [Constructor]
@@ -644,7 +644,7 @@ delegatedProperties.kt:
644644
# 42| 0: [MethodCall] getValue(...)
645645
# 42| -1: [VarAccess] this.varResource0$delegate
646646
# 42| -1: [ThisAccess] this
647-
# 1| 0: [ThisAccess] this
647+
# 42| 0: [ThisAccess] this
648648
# 42| 1: [PropertyRefExpr] ...::...
649649
# 42| -4: [AnonymousClass] new KMutableProperty1<Owner,Integer>(...) { ... }
650650
# 42| 1: [Constructor]
@@ -687,7 +687,7 @@ delegatedProperties.kt:
687687
# 42| 0: [MethodCall] setValue(...)
688688
# 42| -1: [VarAccess] this.varResource0$delegate
689689
# 42| -1: [ThisAccess] this
690-
# 1| 0: [ThisAccess] this
690+
# 42| 0: [ThisAccess] this
691691
# 42| 1: [PropertyRefExpr] ...::...
692692
# 42| -4: [AnonymousClass] new KMutableProperty1<Owner,Integer>(...) { ... }
693693
# 42| 1: [Constructor]
@@ -891,7 +891,7 @@ delegatedProperties.kt:
891891
# 66| -1: [TypeAccess] PropertyReferenceDelegatesKt
892892
# 66| 0: [VarAccess] this.delegatedToMember1$delegate
893893
# 66| -1: [ThisAccess] this
894-
# 1| 1: [ThisAccess] this
894+
# 66| 1: [ThisAccess] this
895895
# 66| 2: [PropertyRefExpr] ...::...
896896
# 66| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
897897
# 66| 1: [Constructor]
@@ -936,7 +936,7 @@ delegatedProperties.kt:
936936
# 66| -1: [TypeAccess] PropertyReferenceDelegatesKt
937937
# 66| 0: [VarAccess] this.delegatedToMember1$delegate
938938
# 66| -1: [ThisAccess] this
939-
# 1| 1: [ThisAccess] this
939+
# 66| 1: [ThisAccess] this
940940
# 66| 2: [PropertyRefExpr] ...::...
941941
# 66| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
942942
# 66| 1: [Constructor]
@@ -1021,7 +1021,7 @@ delegatedProperties.kt:
10211021
# 67| -1: [TypeAccess] PropertyReferenceDelegatesKt
10221022
# 67| 0: [VarAccess] this.delegatedToMember2$delegate
10231023
# 67| -1: [ThisAccess] this
1024-
# 1| 1: [ThisAccess] this
1024+
# 67| 1: [ThisAccess] this
10251025
# 67| 2: [PropertyRefExpr] ...::...
10261026
# 67| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
10271027
# 67| 1: [Constructor]
@@ -1067,7 +1067,7 @@ delegatedProperties.kt:
10671067
# 67| -1: [TypeAccess] PropertyReferenceDelegatesKt
10681068
# 67| 0: [VarAccess] this.delegatedToMember2$delegate
10691069
# 67| -1: [ThisAccess] this
1070-
# 1| 1: [ThisAccess] this
1070+
# 67| 1: [ThisAccess] this
10711071
# 67| 2: [PropertyRefExpr] ...::...
10721072
# 67| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
10731073
# 67| 1: [Constructor]
@@ -1146,7 +1146,7 @@ delegatedProperties.kt:
11461146
# 69| -1: [TypeAccess] PropertyReferenceDelegatesKt
11471147
# 69| 0: [VarAccess] this.delegatedToExtMember1$delegate
11481148
# 69| -1: [ThisAccess] this
1149-
# 1| 1: [ThisAccess] this
1149+
# 69| 1: [ThisAccess] this
11501150
# 69| 2: [PropertyRefExpr] ...::...
11511151
# 69| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
11521152
# 69| 1: [Constructor]
@@ -1191,7 +1191,7 @@ delegatedProperties.kt:
11911191
# 69| -1: [TypeAccess] PropertyReferenceDelegatesKt
11921192
# 69| 0: [VarAccess] this.delegatedToExtMember1$delegate
11931193
# 69| -1: [ThisAccess] this
1194-
# 1| 1: [ThisAccess] this
1194+
# 69| 1: [ThisAccess] this
11951195
# 69| 2: [PropertyRefExpr] ...::...
11961196
# 69| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
11971197
# 69| 1: [Constructor]
@@ -1278,7 +1278,7 @@ delegatedProperties.kt:
12781278
# 70| -1: [TypeAccess] PropertyReferenceDelegatesKt
12791279
# 70| 0: [VarAccess] this.delegatedToExtMember2$delegate
12801280
# 70| -1: [ThisAccess] this
1281-
# 1| 1: [ThisAccess] this
1281+
# 70| 1: [ThisAccess] this
12821282
# 70| 2: [PropertyRefExpr] ...::...
12831283
# 70| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
12841284
# 70| 1: [Constructor]
@@ -1324,7 +1324,7 @@ delegatedProperties.kt:
13241324
# 70| -1: [TypeAccess] PropertyReferenceDelegatesKt
13251325
# 70| 0: [VarAccess] this.delegatedToExtMember2$delegate
13261326
# 70| -1: [ThisAccess] this
1327-
# 1| 1: [ThisAccess] this
1327+
# 70| 1: [ThisAccess] this
13281328
# 70| 2: [PropertyRefExpr] ...::...
13291329
# 70| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
13301330
# 70| 1: [Constructor]
@@ -1405,7 +1405,7 @@ delegatedProperties.kt:
14051405
# 72| -1: [TypeAccess] PropertyReferenceDelegatesKt
14061406
# 72| 0: [VarAccess] this.delegatedToBaseClass1$delegate
14071407
# 72| -1: [ThisAccess] this
1408-
# 1| 1: [ThisAccess] this
1408+
# 72| 1: [ThisAccess] this
14091409
# 72| 2: [PropertyRefExpr] ...::...
14101410
# 72| -4: [AnonymousClass] new KProperty1<MyClass,Integer>(...) { ... }
14111411
# 72| 1: [Constructor]
@@ -1471,7 +1471,7 @@ delegatedProperties.kt:
14711471
# 73| -1: [TypeAccess] PropertyReferenceDelegatesKt
14721472
# 73| 0: [VarAccess] this.delegatedToBaseClass2$delegate
14731473
# 73| -1: [ThisAccess] this
1474-
# 1| 1: [ThisAccess] this
1474+
# 73| 1: [ThisAccess] this
14751475
# 73| 2: [PropertyRefExpr] ...::...
14761476
# 73| -4: [AnonymousClass] new KProperty1<MyClass,Integer>(...) { ... }
14771477
# 73| 1: [Constructor]
@@ -1531,7 +1531,7 @@ delegatedProperties.kt:
15311531
# 75| -1: [TypeAccess] PropertyReferenceDelegatesKt
15321532
# 75| 0: [VarAccess] this.delegatedToAnotherClass1$delegate
15331533
# 75| -1: [ThisAccess] this
1534-
# 1| 1: [ThisAccess] this
1534+
# 75| 1: [ThisAccess] this
15351535
# 75| 2: [PropertyRefExpr] ...::...
15361536
# 75| -4: [AnonymousClass] new KProperty1<MyClass,Integer>(...) { ... }
15371537
# 75| 1: [Constructor]
@@ -1597,7 +1597,7 @@ delegatedProperties.kt:
15971597
# 77| -1: [TypeAccess] PropertyReferenceDelegatesKt
15981598
# 77| 0: [VarAccess] this.delegatedToTopLevel$delegate
15991599
# 77| -1: [ThisAccess] this
1600-
# 1| 1: [ThisAccess] this
1600+
# 77| 1: [ThisAccess] this
16011601
# 77| 2: [PropertyRefExpr] ...::...
16021602
# 77| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
16031603
# 77| 1: [Constructor]
@@ -1642,7 +1642,7 @@ delegatedProperties.kt:
16421642
# 77| -1: [TypeAccess] PropertyReferenceDelegatesKt
16431643
# 77| 0: [VarAccess] this.delegatedToTopLevel$delegate
16441644
# 77| -1: [ThisAccess] this
1645-
# 1| 1: [ThisAccess] this
1645+
# 77| 1: [ThisAccess] this
16461646
# 77| 2: [PropertyRefExpr] ...::...
16471647
# 77| -4: [AnonymousClass] new KMutableProperty1<MyClass,Integer>(...) { ... }
16481648
# 77| 1: [Constructor]
@@ -1713,7 +1713,7 @@ delegatedProperties.kt:
17131713
# 79| -1: [TypeAccess] PropertyReferenceDelegatesKt
17141714
# 79| 0: [VarAccess] this.max$delegate
17151715
# 79| -1: [ThisAccess] this
1716-
# 1| 1: [ThisAccess] this
1716+
# 79| 1: [ThisAccess] this
17171717
# 79| 2: [PropertyRefExpr] ...::...
17181718
# 79| -4: [AnonymousClass] new KProperty1<MyClass,Integer>(...) { ... }
17191719
# 79| 1: [Constructor]

0 commit comments

Comments
 (0)