mirror of
https://github.com/github/codeql.git
synced 2026-07-12 23:15:40 +02:00
Kotlin: converge delegated property accessor locations onto K2 property span
For local and member delegated properties, the synthesised accessor (`<get-prop>`/`<set-prop>`) and its generated wrapper class/constructor were anchored by the K1 frontend at the delegate expression rather than at the property declaration. K2 (raw IR) anchors them at the property `val`/`var` keyword through the end of the delegate, i.e. the whole `KtProperty` span. Adopt the K2 span for both frontends so the extractor emits identical locations regardless of the supplied language version. Example: <get-prop1> 6:24:9:9 -> 6:9:9:9 Add `getPsiBasedDelegatedAccessorLocation`, which walks from the accessor's PSI up to the enclosing `KtProperty` and returns its span. It returns null when there is no PSI (K2) or no enclosing property, so K2 keeps its native locations and non-delegated callables are unaffected. Wire it into `extractFunction`'s location chain and into `extractGeneratedClass` (guarded on `DELEGATED_PROPERTY_ACCESSOR`) so the generated class/constructor sorts before the method, matching K2. Updates test-kotlin1 expected only (test-kotlin2 unchanged). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2463,6 +2463,7 @@ open class KotlinFileExtractor(
|
||||
|
||||
val locId =
|
||||
overriddenAttributes?.sourceLoc
|
||||
?: (if (f.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR) getPsiBasedDelegatedAccessorLocation(f) else null)
|
||||
?: (if (usesK2) getPsiBasedLocation(f) else null)
|
||||
?: (if (f.symbol is IrConstructorSymbol && typeSubstitution == null) getPsiBasedImplicitConstructorLocation(f) else null)
|
||||
?: getLocation(f, classTypeArgsIncludingOuterClasses)
|
||||
@@ -3076,6 +3077,42 @@ open class KotlinFileExtractor(
|
||||
return tw.getLocation(ktClass.startOffset, ktClass.endOffset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PSI-based location for the synthesised getter/setter of a
|
||||
* *delegated* property (origin `DELEGATED_PROPERTY_ACCESSOR`), spanning the
|
||||
* enclosing property declaration from its `val`/`var` keyword through the end of
|
||||
* the delegate expression.
|
||||
*
|
||||
* For a delegated property such as `val prop1: Int by lazy { ... }` the compiler
|
||||
* synthesises a getter (and, for `var`, a setter) whose body forwards to the
|
||||
* delegate's `getValue`/`setValue`. The K2 frontend records these accessors with
|
||||
* raw IR offsets spanning the whole property declaration (the `val`/`var` keyword
|
||||
* through the delegate expression). The K1 frontend's raw offsets (and its
|
||||
* [findPsiElement]-based location) instead cover only the delegate expression, so
|
||||
* the two frontends disagree on the accessor's start column.
|
||||
*
|
||||
* K1 retains the PSI, so we recover the K2-native span from the enclosing
|
||||
* [KtProperty]: the [findPsiElement] mapping resolves the accessor to a node
|
||||
* within the property's delegate, from which we walk up to the [KtProperty].
|
||||
* [KtProperty.valOrVarKeyword] then gives the start (excluding any leading
|
||||
* modifiers, matching the default-accessor and property spans) and
|
||||
* [KtProperty.endOffset] gives the end (past the delegate expression).
|
||||
*
|
||||
* This is deliberately not gated on [usesK2]: it must apply to the K1 frontends
|
||||
* (which retain PSI) to converge them onto the K2-native span, and it returns null
|
||||
* under K2 (where [findPsiElement] finds no PSI and the raw offsets already carry
|
||||
* this span). It applies uniformly to both getter and setter, and to both
|
||||
* member-level and local delegated properties.
|
||||
*/
|
||||
private fun getPsiBasedDelegatedAccessorLocation(f: IrFunction): Label<DbLocation>? {
|
||||
val file = currentIrFile ?: return null
|
||||
val psiElement = getPsi2Ir()?.findPsiElement(f, file) ?: return null
|
||||
val ktProperty = generateSequence(psiElement) { it.parent }
|
||||
.filterIsInstance<KtProperty>().firstOrNull()
|
||||
?: return null
|
||||
return tw.getLocation(ktProperty.valOrVarKeyword.startOffset, ktProperty.endOffset)
|
||||
}
|
||||
|
||||
private fun extractVariable(
|
||||
v: IrVariable,
|
||||
callable: Label<out DbCallable>,
|
||||
@@ -9372,11 +9409,21 @@ open class KotlinFileExtractor(
|
||||
with("generated class", localFunction) {
|
||||
val ids = getLocallyVisibleFunctionLabels(localFunction)
|
||||
|
||||
// For a delegated property's synthesised accessor the generated wrapper class
|
||||
// (and its constructor and super-call) must share the accessor's declaration
|
||||
// span so that, like the K2 frontend, the constructor still sorts before the
|
||||
// getter/setter rather than after it (the raw K1 offset would place the class
|
||||
// at the delegate expression, past the accessor's own start column).
|
||||
val classLocId =
|
||||
(if (localFunction.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR)
|
||||
getPsiBasedDelegatedAccessorLocation(localFunction) else null)
|
||||
?: tw.getLocation(localFunction)
|
||||
|
||||
val id =
|
||||
extractGeneratedClass(
|
||||
ids,
|
||||
superTypes,
|
||||
tw.getLocation(localFunction),
|
||||
classLocId,
|
||||
localFunction,
|
||||
localFunction.parent,
|
||||
compilerGeneratedKindOverride = compilerGeneratedKindOverride
|
||||
|
||||
Reference in New Issue
Block a user