Skip to content

Commit 5626a5d

Browse files
Kotlin: locate generated data-class copy params at their property
A data class's generated `copy(...)` has one value parameter per primary-constructor property. The K1 frontend records each such parameter (and its type accesses) at the source location of the corresponding property; the K2 frontend leaves them with undefined offsets, which the extractor emits as a `0:0:0:0` location. This divergence is purely a K2 information regression: the richer K1 location is unambiguously better (it points at the real property in source, enabling location-based queries), so we converge K2 onto K1 rather than the other way around. Because K2 exposes no PSI back-mapping, the location cannot be recomputed from source; instead we recover it from the IR. For a value parameter of a `GENERATED_DATA_CLASS_MEMBER` function whose own offsets are undefined, we look up the primary-constructor parameter at the same index and reuse its location. Guards keep the change surgical: - `vp.startOffset >= 0` bails out, so K1 (which already has real offsets) is untouched. - the origin must be `GENERATED_DATA_CLASS_MEMBER`. - the primary-ctor parameter name must match and carry real offsets, which restricts the remap to `copy`-style parameters and excludes members such as `equals(other)`. Relearned both suites: only data-class `copy` parameter rows change (K2 now matches K1). data-classes/PrintAst.expected becomes byte -identical across suites; the residual diffs in methods/{exprs, parameters}.expected are pre-existing, unrelated divergences. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2e814a7 commit 5626a5d

4 files changed

Lines changed: 42 additions & 10 deletions

File tree

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,35 @@ open class KotlinFileExtractor(
13291329
private fun hasSynthesizedParameterNames(f: IrFunction) =
13301330
f.descriptor.hasSynthesizedParameterNames()
13311331

1332+
/**
1333+
* Returns the location of the primary-constructor parameter corresponding to a generated
1334+
* data-class `copy` value parameter, or null if [vp] is not such a parameter.
1335+
*
1336+
* A data class's generated `copy(...)` has one value parameter per primary-constructor
1337+
* property, defaulting to the current value. K1 records these parameters at the source
1338+
* location of the corresponding property; K2 leaves them with undefined offsets (a
1339+
* `0:0:0:0` location). To keep the richer K1 information under both frontends, we recover
1340+
* the property location from the primary constructor via the IR.
1341+
*
1342+
* Guards ensure this only affects the K2 case (K1 parameters already carry real offsets)
1343+
* and only `copy`-style parameters (matched by name against the primary-constructor
1344+
* parameter at the same index), so members such as `equals(other)` are left untouched.
1345+
*/
1346+
private fun getGeneratedDataClassCopyParamLocation(
1347+
vp: IrValueParameter,
1348+
idx: Int
1349+
): Label<DbLocation>? {
1350+
if (vp.startOffset >= 0) return null
1351+
val fn = vp.parent as? IrFunction ?: return null
1352+
if (fn.origin != IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER) return null
1353+
val ctorParam =
1354+
fn.parentClassOrNull?.primaryConstructor?.codeQlValueParameters?.getOrNull(idx)
1355+
?: return null
1356+
if (ctorParam.name.asString() != vp.name.asString() || ctorParam.startOffset < 0)
1357+
return null
1358+
return tw.getLocation(ctorParam)
1359+
}
1360+
13321361
private fun extractValueParameter(
13331362
vp: IrValueParameter,
13341363
parent: Label<out DbCallable>,
@@ -1340,7 +1369,10 @@ open class KotlinFileExtractor(
13401369
locOverride: Label<DbLocation>? = null
13411370
): TypeResults {
13421371
with("value parameter", vp) {
1343-
val location = locOverride ?: getLocation(vp, classTypeArgsIncludingOuterClasses)
1372+
val location =
1373+
locOverride
1374+
?: getGeneratedDataClassCopyParamLocation(vp, idx)
1375+
?: getLocation(vp, classTypeArgsIncludingOuterClasses)
13441376
val maybeAlteredType =
13451377
(vp.parent as? IrFunction)?.let {
13461378
if (overridesCollectionsMethodWithAlteredParameterTypes(it))

java/ql/test-kotlin2/library-tests/data-classes/PrintAst.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ dc.kt:
1717
# 0| 3: [Method] copy
1818
# 0| 3: [TypeAccess] ProtoMapValue
1919
#-----| 4: (Parameters)
20-
# 0| 0: [Parameter] bytes
21-
# 0| 0: [TypeAccess] byte[]
22-
# 0| 1: [Parameter] strs
23-
# 0| 0: [TypeAccess] String[]
24-
# 0| 0: [TypeAccess] String
20+
# 1| 0: [Parameter] bytes
21+
# 1| 0: [TypeAccess] byte[]
22+
# 1| 1: [Parameter] strs
23+
# 1| 0: [TypeAccess] String[]
24+
# 1| 0: [TypeAccess] String
2525
# 0| 5: [BlockStmt] { ... }
2626
# 0| 0: [ReturnStmt] return ...
2727
# 0| 0: [ClassInstanceExpr] new ProtoMapValue(...)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
| dataClass.kt:0:0:0:0 | String | TypeAccess |
4949
| dataClass.kt:0:0:0:0 | String | TypeAccess |
5050
| dataClass.kt:0:0:0:0 | String | TypeAccess |
51-
| dataClass.kt:0:0:0:0 | String | TypeAccess |
5251
| dataClass.kt:0:0:0:0 | boolean | TypeAccess |
5352
| dataClass.kt:0:0:0:0 | copy(...) | MethodCall |
5453
| dataClass.kt:0:0:0:0 | false | BooleanLiteral |
@@ -60,7 +59,6 @@
6059
| dataClass.kt:0:0:0:0 | int | TypeAccess |
6160
| dataClass.kt:0:0:0:0 | int | TypeAccess |
6261
| dataClass.kt:0:0:0:0 | int | TypeAccess |
63-
| dataClass.kt:0:0:0:0 | int | TypeAccess |
6462
| dataClass.kt:0:0:0:0 | new DataClass(...) | ClassInstanceExpr |
6563
| dataClass.kt:0:0:0:0 | other | VarAccess |
6664
| dataClass.kt:0:0:0:0 | other | VarAccess |
@@ -114,6 +112,7 @@
114112
| dataClass.kt:1:22:1:31 | int | TypeAccess |
115113
| dataClass.kt:1:22:1:31 | int | TypeAccess |
116114
| dataClass.kt:1:22:1:31 | int | TypeAccess |
115+
| dataClass.kt:1:22:1:31 | int | TypeAccess |
117116
| dataClass.kt:1:22:1:31 | this | ThisAccess |
118117
| dataClass.kt:1:22:1:31 | this.x | VarAccess |
119118
| dataClass.kt:1:22:1:31 | x | VarAccess |
@@ -125,6 +124,7 @@
125124
| dataClass.kt:1:34:1:46 | String | TypeAccess |
126125
| dataClass.kt:1:34:1:46 | String | TypeAccess |
127126
| dataClass.kt:1:34:1:46 | String | TypeAccess |
127+
| dataClass.kt:1:34:1:46 | String | TypeAccess |
128128
| dataClass.kt:1:34:1:46 | Unit | TypeAccess |
129129
| dataClass.kt:1:34:1:46 | this | ThisAccess |
130130
| dataClass.kt:1:34:1:46 | this | ThisAccess |

java/ql/test-kotlin2/library-tests/methods/parameters.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
| clinit.kt:3:1:3:20 | setTopLevelInt | clinit.kt:3:1:3:20 | <set-?> | 0 |
2-
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:0:0:0:0 | x | 0 |
3-
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:0:0:0:0 | y | 1 |
2+
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:1:22:1:31 | x | 0 |
3+
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:1:34:1:46 | y | 1 |
44
| dataClass.kt:0:0:0:0 | copy$default | dataClass.kt:0:0:0:0 | p0 | 0 |
55
| dataClass.kt:0:0:0:0 | copy$default | dataClass.kt:0:0:0:0 | p1 | 1 |
66
| dataClass.kt:0:0:0:0 | copy$default | dataClass.kt:0:0:0:0 | p2 | 2 |

0 commit comments

Comments
 (0)