kotlin-extractor: converge backing-field locations across language versions

Anchor a property backing field's location on its property declaration (the
`val`/`var` keyword to the end of the declaration) rather than the raw IR
offset. The raw `IrField` offset is inconsistent between frontends: under
`-language-version 1.9` it includes leading modifiers in the span start, while
under 2.0 it starts at the `val`/`var` keyword.

Example: `private val privateProp: Int = 0`

  before (lang 1.9):  properties.kt:35:5:35:32  | int privateProp;  (col 5 = `private`)
  after  (lang 1.9):  properties.kt:35:13:35:32 | int privateProp;  (col 13 = `val`)
  lang 2.0 (unchanged): properties.kt:35:13:35:32 | int privateProp;

The property entity already uses this PSI-based anchor (getPsiBasedLocation),
so the field now matches its own property location, which is what the 2.0
frontend already emits.

Delegated properties are excluded via `isDelegated`: their field is the
`$delegate` storage, whose location is the delegate expression rather than the
property declaration, and is converged separately.

This is a no-op for `-language-version 2.0` (only test-kotlin1 expected files
change); the two suites' backing-field and field-type-access locations now
agree.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Anders Fugmann
2026-07-10 21:52:45 +02:00
parent 327eedcc9b
commit 7e79e693f4
10 changed files with 61 additions and 49 deletions

View File

@@ -2594,12 +2594,24 @@ open class KotlinFileExtractor(
if (isAnnotationClassField(f)) kClassToJavaClass(f.type) else f.type
val id = useField(f)
extractAnnotations(f, id, extractAnnotationEnumTypeAccesses)
// A plain backing field's location should match its property's location
// (the `val`/`var` keyword to the end of the declaration), rather than the
// raw IR offset, which includes leading modifiers under -language-version
// 1.9 but not 2.0. Anchoring on the property keeps the two consistent.
// Delegated properties are excluded: their field is the `$delegate` storage,
// whose location is the delegate expression, not the property declaration.
val locId =
f.correspondingPropertySymbol
?.owner
?.takeUnless { it.isDelegated }
?.let { getPsiBasedLocation(it) }
?: tw.getLocation(f)
return extractField(
id,
"${f.name.asString()}$fNameSuffix",
extractType,
parentId,
tw.getLocation(f),
locId,
f.visibility,
f,
isExternalDeclaration(f),