mirror of
https://github.com/github/codeql.git
synced 2026-07-13 23:38:15 +02:00
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>
This commit is contained in:
@@ -1329,6 +1329,35 @@ open class KotlinFileExtractor(
|
||||
private fun hasSynthesizedParameterNames(f: IrFunction) =
|
||||
f.descriptor.hasSynthesizedParameterNames()
|
||||
|
||||
/**
|
||||
* Returns the location of the primary-constructor parameter corresponding to a generated
|
||||
* data-class `copy` value parameter, or null if [vp] is not such a parameter.
|
||||
*
|
||||
* A data class's generated `copy(...)` has one value parameter per primary-constructor
|
||||
* property, defaulting to the current value. K1 records these parameters at the source
|
||||
* location of the corresponding property; K2 leaves them with undefined offsets (a
|
||||
* `0:0:0:0` location). To keep the richer K1 information under both frontends, we recover
|
||||
* the property location from the primary constructor via the IR.
|
||||
*
|
||||
* Guards ensure this only affects the K2 case (K1 parameters already carry real offsets)
|
||||
* and only `copy`-style parameters (matched by name against the primary-constructor
|
||||
* parameter at the same index), so members such as `equals(other)` are left untouched.
|
||||
*/
|
||||
private fun getGeneratedDataClassCopyParamLocation(
|
||||
vp: IrValueParameter,
|
||||
idx: Int
|
||||
): Label<DbLocation>? {
|
||||
if (vp.startOffset >= 0) return null
|
||||
val fn = vp.parent as? IrFunction ?: return null
|
||||
if (fn.origin != IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER) return null
|
||||
val ctorParam =
|
||||
fn.parentClassOrNull?.primaryConstructor?.codeQlValueParameters?.getOrNull(idx)
|
||||
?: return null
|
||||
if (ctorParam.name.asString() != vp.name.asString() || ctorParam.startOffset < 0)
|
||||
return null
|
||||
return tw.getLocation(ctorParam)
|
||||
}
|
||||
|
||||
private fun extractValueParameter(
|
||||
vp: IrValueParameter,
|
||||
parent: Label<out DbCallable>,
|
||||
@@ -1340,7 +1369,10 @@ open class KotlinFileExtractor(
|
||||
locOverride: Label<DbLocation>? = null
|
||||
): TypeResults {
|
||||
with("value parameter", vp) {
|
||||
val location = locOverride ?: getLocation(vp, classTypeArgsIncludingOuterClasses)
|
||||
val location =
|
||||
locOverride
|
||||
?: getGeneratedDataClassCopyParamLocation(vp, idx)
|
||||
?: getLocation(vp, classTypeArgsIncludingOuterClasses)
|
||||
val maybeAlteredType =
|
||||
(vp.parent as? IrFunction)?.let {
|
||||
if (overridesCollectionsMethodWithAlteredParameterTypes(it))
|
||||
|
||||
@@ -17,11 +17,11 @@ dc.kt:
|
||||
# 0| 3: [Method] copy
|
||||
# 0| 3: [TypeAccess] ProtoMapValue
|
||||
#-----| 4: (Parameters)
|
||||
# 0| 0: [Parameter] bytes
|
||||
# 0| 0: [TypeAccess] byte[]
|
||||
# 0| 1: [Parameter] strs
|
||||
# 0| 0: [TypeAccess] String[]
|
||||
# 0| 0: [TypeAccess] String
|
||||
# 1| 0: [Parameter] bytes
|
||||
# 1| 0: [TypeAccess] byte[]
|
||||
# 1| 1: [Parameter] strs
|
||||
# 1| 0: [TypeAccess] String[]
|
||||
# 1| 0: [TypeAccess] String
|
||||
# 0| 5: [BlockStmt] { ... }
|
||||
# 0| 0: [ReturnStmt] return ...
|
||||
# 0| 0: [ClassInstanceExpr] new ProtoMapValue(...)
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
| dataClass.kt:0:0:0:0 | String | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | String | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | String | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | String | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | boolean | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | copy(...) | MethodCall |
|
||||
| dataClass.kt:0:0:0:0 | false | BooleanLiteral |
|
||||
@@ -60,7 +59,6 @@
|
||||
| dataClass.kt:0:0:0:0 | int | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | int | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | int | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | int | TypeAccess |
|
||||
| dataClass.kt:0:0:0:0 | new DataClass(...) | ClassInstanceExpr |
|
||||
| dataClass.kt:0:0:0:0 | other | VarAccess |
|
||||
| dataClass.kt:0:0:0:0 | other | VarAccess |
|
||||
@@ -114,6 +112,7 @@
|
||||
| dataClass.kt:1:22:1:31 | int | TypeAccess |
|
||||
| dataClass.kt:1:22:1:31 | int | TypeAccess |
|
||||
| dataClass.kt:1:22:1:31 | int | TypeAccess |
|
||||
| dataClass.kt:1:22:1:31 | int | TypeAccess |
|
||||
| dataClass.kt:1:22:1:31 | this | ThisAccess |
|
||||
| dataClass.kt:1:22:1:31 | this.x | VarAccess |
|
||||
| dataClass.kt:1:22:1:31 | x | VarAccess |
|
||||
@@ -125,6 +124,7 @@
|
||||
| dataClass.kt:1:34:1:46 | String | TypeAccess |
|
||||
| dataClass.kt:1:34:1:46 | String | TypeAccess |
|
||||
| dataClass.kt:1:34:1:46 | String | TypeAccess |
|
||||
| dataClass.kt:1:34:1:46 | String | TypeAccess |
|
||||
| dataClass.kt:1:34:1:46 | Unit | TypeAccess |
|
||||
| dataClass.kt:1:34:1:46 | this | ThisAccess |
|
||||
| dataClass.kt:1:34:1:46 | this | ThisAccess |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
| clinit.kt:3:1:3:20 | setTopLevelInt | clinit.kt:3:1:3:20 | <set-?> | 0 |
|
||||
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:0:0:0:0 | x | 0 |
|
||||
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:0:0:0:0 | y | 1 |
|
||||
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:1:22:1:31 | x | 0 |
|
||||
| dataClass.kt:0:0:0:0 | copy | dataClass.kt:1:34:1:46 | y | 1 |
|
||||
| dataClass.kt:0:0:0:0 | copy$default | dataClass.kt:0:0:0:0 | p0 | 0 |
|
||||
| dataClass.kt:0:0:0:0 | copy$default | dataClass.kt:0:0:0:0 | p1 | 1 |
|
||||
| dataClass.kt:0:0:0:0 | copy$default | dataClass.kt:0:0:0:0 | p2 | 2 |
|
||||
|
||||
Reference in New Issue
Block a user