Skip to content

Commit db88f07

Browse files
Kotlin: include accessor annotations in explicit accessor location
An explicit property accessor may carry its own annotations, for example: val x: Int @JvmName("getX_prop") get() = 15 The K2 frontend records such an accessor with raw IR offsets that begin at the leading annotation; the K1 frontend's raw offsets start at the `get`/`set` keyword and omit the annotation. This is a pure K1 information regression: the annotation is part of the accessor declaration and the K2 span is the more faithful one, so we converge K1 onto K2. Because the annotation-inclusive start cannot be reconstructed under K2 (no PSI back-mapping) but is trivially available under K1, we recover it from the KtPropertyAccessor PSI node, whose text range begins at its modifier list. A new helper getPsiBasedAnnotatedAccessorLocation returns this span, and accessorOverride now applies it to explicit accessors (in addition to the existing synthesised-accessor handling). Guards keep the change surgical: - returns null under K2 (getKtFile unavailable; raw offsets already include the annotation), leaving K2 untouched. - returns null when the accessor declares no annotations of its own, so non-annotated explicit accessors (which already converge) are unaffected. Relearned both suites: only explicit annotated-accessor declaration rows change (K1 now matches K2). annotations/jvmName/test.expected becomes byte-identical across suites; the residual diffs in jvmstatic-annotation are pre-existing, unrelated divergences (JVM-static proxy forwarder locations and call-argument spans). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5626a5d commit db88f07

3 files changed

Lines changed: 36 additions & 7 deletions

File tree

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,9 +2762,10 @@ open class KotlinFileExtractor(
27622762
// getPsiBasedAccessorLocation): the keyword token for a bare `get`/`set`,
27632763
// or the property signature for a fully synthesised accessor.
27642764
fun accessorOverride(f: IrFunction) =
2765-
if (f.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR)
2766-
getPsiBasedAccessorLocation(p, f)?.let { OverriddenFunctionAttributes(sourceLoc = it) }
2767-
else null
2765+
(if (f.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR)
2766+
getPsiBasedAccessorLocation(p, f)
2767+
else getPsiBasedAnnotatedAccessorLocation(p, f))
2768+
?.let { OverriddenFunctionAttributes(sourceLoc = it) }
27682769

27692770
if (getter == null) {
27702771
if (!isExternalDeclaration(p)) {
@@ -3132,9 +3133,37 @@ open class KotlinFileExtractor(
31323133
return tw.getLocation(ktClass.startOffset, ktClass.endOffset)
31333134
}
31343135

3136+
/**
3137+
* Returns the PSI-based location for an *explicit* property accessor that carries
3138+
* its own annotation(s) (for example `@JvmName("getX_prop") get() = 15`), spanning
3139+
* from the leading annotation through the end of the accessor.
3140+
*
3141+
* The K2 frontend records such an accessor with raw IR offsets that already include
3142+
* the leading annotation; the K1 frontend's raw offsets start at the `get`/`set`
3143+
* keyword and omit the annotation. We converge K1 onto the annotation-inclusive K2
3144+
* span by recovering it from the [KtPropertyAccessor] PSI node, whose text range
3145+
* begins at its modifier list (the annotations).
3146+
*
3147+
* Returns null under K2 (where [getKtFile] is unavailable and the raw offsets
3148+
* already carry the annotation) and for accessors that declare no annotations of
3149+
* their own (which both frontends already record identically).
3150+
*/
3151+
private fun getPsiBasedAnnotatedAccessorLocation(p: IrProperty, accessor: IrFunction): Label<DbLocation>? {
3152+
if (p.startOffset < 0 || p.endOffset < 0) return null
3153+
val file = currentIrFile ?: return null
3154+
val ktFile = getPsi2Ir()?.getKtFile(file) ?: return null
3155+
val ktProperty = ktFile.findElementAt(p.startOffset)
3156+
?.let { leaf -> generateSequence(leaf) { it.parent }.filterIsInstance<KtProperty>().firstOrNull() }
3157+
?: return null
3158+
val isGetter = p.getter?.symbol == accessor.symbol
3159+
val ktAccessor: KtPropertyAccessor = (if (isGetter) ktProperty.getter else ktProperty.setter)
3160+
?: return null
3161+
if (ktAccessor.annotationEntries.isEmpty()) return null
3162+
return tw.getLocation(ktAccessor.startOffset, ktAccessor.endOffset)
3163+
}
3164+
31353165
/**
31363166
* Returns the PSI-based location for the synthesised getter/setter of a
3137-
* *delegated* property (origin `DELEGATED_PROPERTY_ACCESSOR`), spanning the
31383167
* enclosing property declaration from its `val`/`var` keyword through the end of
31393168
* the delegate expression.
31403169
*

java/ql/test-kotlin1/library-tests/annotations/jvmName/test.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
| Test.java:2:17:2:17 | m | m | m |
2-
| test.kt:4:9:4:18 | getX_prop | getX_prop | getX |
2+
| test.kt:3:9:4:18 | getX_prop | getX_prop | getX |
33
| test.kt:6:5:6:19 | getX | getX | getX |
44
| test.kt:10:5:10:14 | changeY | changeY | setY |
55
| test.kt:10:5:10:14 | y | y | getY |

java/ql/test-kotlin1/library-tests/jvmstatic-annotation/test.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ staticMembers
1212
| test.kt:31:1:47:1 | NonCompanion | test.kt:33:14:33:69 | staticMethod | Method |
1313
| test.kt:31:1:47:1 | NonCompanion | test.kt:36:14:36:35 | getStaticProp | Method |
1414
| test.kt:31:1:47:1 | NonCompanion | test.kt:36:14:36:35 | setStaticProp | Method |
15-
| test.kt:31:1:47:1 | NonCompanion | test.kt:40:16:40:43 | getPropWithStaticGetter | Method |
16-
| test.kt:31:1:47:1 | NonCompanion | test.kt:45:16:45:58 | setPropWithStaticSetter | Method |
15+
| test.kt:31:1:47:1 | NonCompanion | test.kt:40:5:40:43 | getPropWithStaticGetter | Method |
16+
| test.kt:31:1:47:1 | NonCompanion | test.kt:45:5:45:58 | setPropWithStaticSetter | Method |
1717
#select
1818
| test.kt:9:1:29:1 | HasCompanion | JavaUser.java:5:5:5:34 | staticMethod(...) | JavaUser.java:5:5:5:16 | HasCompanion | static |
1919
| test.kt:9:1:29:1 | HasCompanion | JavaUser.java:7:5:7:73 | setStaticProp(...) | JavaUser.java:7:5:7:16 | HasCompanion | static |

0 commit comments

Comments
 (0)