Commit Graph

7 Commits

Author SHA1 Message Date
Anders Fugmann
240103e7b3 Kotlin: converge implicit primary constructor locations onto the K2-native class span
The K1 and K2 frontends record different source locations for the
compiler-synthesised primary constructor of a class that declares no primary
constructor in source. K2 uses the class declaration's raw IR offsets, which
include any leading modifier keywords, while K1's raw offsets start at the
`class` keyword and omit the modifiers.

Since K1 (unlike K2) retains the PSI, we recover the modifier-inclusive span
from the enclosing KtClassOrObject so K1 matches the K2-native span.

Example, for `open class C0<V> {}` on line 11 (the `open` modifier is at
column 1, the `class` keyword at column 6):

  before (K1):  generics.kt:11:6:11:19 | C0 | C0()
  after  (K1):  generics.kt:11:1:11:19 | C0 | C0()   (matches K2)

The fix is deliberately narrow and leaves all other constructors untouched:
 - explicit primary constructors (`class C1(val t: T)`, `class C2()`) keep
   their own parameter-list location, which both frontends already agree on;
 - explicit secondary constructors keep their own location (only the
   `isPrimary` constructor is adjusted);
 - specialised/parameterised copies of a generic constructor
   (`typeSubstitution != null`) are excluded, so they do not gain a spurious
   source location and appear in source-filtered queries.

Relearned test-kotlin1 (K1) and test-kotlin2 (K2): all 3333 tests pass with
database-consistency checks. Only test-kotlin1 expected files change (K2 output
is unchanged, as K2 already emits these spans natively). No previously matching
row diverges; net K1-vs-K2 divergence decreases on every affected file, with
generics, generic-inner-classes and modifiers now fully identical.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-11 10:12:41 +02:00
Anders Fugmann
e37d89b849 Kotlin: converge default property-accessor locations onto the K2-native spans
Synthesised and bare `get`/`set` accessors were extracted with different
source locations depending on the frontend:

    val typedProp: Int = 3            // getTypedProp
        K1: 5:5:5:17  (val..name)     K2: 5:5:5:22  (val..type)

    val defaultGetter = 7
        get                           // getDefaultGetter
        K1: 19:5:19:21 (property head) K2: 20:13:20:15 (`get` keyword)

Under K2 the extractor has no PSI back-mapping for these accessors
(`getKtFile` returns null), so it cannot reproduce K1's property-name-end
span; K2 instead falls back to the raw IR offsets. Rather than converge on a
value K2 cannot produce, K1 is made to match the K2-native spans via the PSI:

  * a bare `get`/`set` keyword now points at the keyword token; and
  * a fully synthesised accessor now spans the property signature
    (`val`/`var` .. type annotation, or .. name when untyped), excluding the
    initialiser.

Explicit-body accessors (`get() = 5`) are unaffected: they are located at
their body and never take this override.

Only K1 output changes; the test-kotlin2 (K2) expected files are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-11 01:46:59 +02:00
Anders Fugmann
19774cc84b 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>
2026-07-10 22:35:28 +02:00
Anders Fugmann
b8a16923ba kotlin-extractor: converge local variable entity locations across language versions
The local variable declaration expression (extractVariableExpr) computed its
location via getPsiBasedLocation(v as IrElement). The `as IrElement` cast forced
overload resolution to the generic getPsiBasedLocation(IrElement), which resolves
the PSI element straight from the IR element's raw source offsets via
PsiSourceManager.findPsiElement. Those offsets differ between the two frontend
language versions:

  fun f(param: Int) {
      val local1 = 2 + 3   // line 6: `        val local1 = 2 + 3`
  }

- With -language-version 1.9 the IrVariable offsets cover only the name, so
  findPsiElement returns the name leaf and the variable is located at 6:13:6:18.
- With -language-version 2.0 the IrVariable offsets cover the whole declaration,
  so it is located at 6:9:6:26 (the `val` keyword through the initialiser).

The IrVariable-specific overload getPsiBasedLocation(IrVariable) is frontend
stable: it finds the leaf at the variable's start offset, walks up to the
enclosing KtVariableDeclaration and returns the span from the `val`/`var` keyword
to the end of the declaration. Using it for the entity location makes both
language versions emit the full declaration span 6:9:6:26.

This is the same helper already used for the enclosing localvariabledeclstmt
location; this change applies it to the variable entity (localvars / the
localvariabledeclexpr) as well, so the two are consistent.

Effect: 12 test-kotlin1 expected files move toward the test-kotlin2 output (all
strictly reduce the tk1-vs-tk2 difference; e.g. variables 16->4, reflection
111->67, exprs 1759->1363 diff lines). test-kotlin2 output is unchanged (the
overload produces the same span there as the generic one did). Both suites pass
all 3333 tests with --check-databases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Also update the enhanced-nullability Kotlin integration test expected: its
local variable declaration expression now spans the val/var keyword to the
initialiser (user.kt:2:7:2:7 -> user.kt:2:3:2:16), consistent with the
library-test convergence.
2026-07-10 22:35:20 +02:00
Anders Fugmann
7eb71807cd fix property and accessor locations using PSI
K1 IrProperty.startOffset includes leading modifiers (private, abstract,
lateinit, annotations) in the span start; K2 already starts at val/var.
Walk the PSI tree from p.startOffset to the enclosing KtProperty, then use
valOrVarKeyword.startOffset as the declaration start, giving a consistent
start in both K1 and K2.

Two related but distinct locations are derived from the KtProperty:

- The property itself spans val/var through the end of the full
  declaration (KtProperty.endOffset), including an explicit getter/setter
  body on a following line. This is getPsiBasedLocation(IrProperty).
- Synthesised accessors (DEFAULT_PROPERTY_ACCESSOR origin) span val/var
  through the end of the property name (KtProperty.nameIdentifier.endOffset)
  via getPsiBasedAccessorLocation, applied through accessorOverride().
  Explicit getter/setter bodies keep their own independently computed
  location.

This makes K1 accessor locations match K2 and gives each synthesised
accessor a precise span, rather than the property's full declaration span.

Example (properties.kt line 3, "var modifiableInt = 1"):
  property  modifiableInt     -> 3:5:3:25   (val/var .. end of "= 1")
  accessor  getModifiableInt  -> 3:5:3:21   (val/var .. end of name)
  accessor  setModifiableInt  -> 3:5:3:21

Because accessor locations appear wherever accessors are reported, this
refinement updates many expected files (property listings, modifiers,
methods, reflection, control-flow and expression dumps). Every change is a
location-coordinate change only: no result tuple is added or removed.

The PSI-based location is restricted to unspecialised extractions
(classTypeArgsIncludingOuterClasses.isNullOrEmpty()). Specialised generic
instances (e.g. C<String>.prop) continue to use the binary whole-file
location returned by getLocation(p, typeArgs), preserving the existing
behaviour that keeps them absent from fromSource() queries.

The visibility merge in extractFunction is extended to accept an
overriddenAttributes parameter from the caller; the internal fake-override
visibility adjustment (DescriptorVisibilities.PUBLIC for Java binary Object
methods) is merged with any caller-supplied attributes so that neither
overrides the other silently.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-10 19:59:14 +02:00
Anders Fugmann
19092fbed7 kotlin-extractor: fix K1 local variable locations to span from val/var to initializer
In K1 mode, IrVariable.startOffset points to the name identifier and
IrVariable.endOffset is also at the name end, giving a location that
covers only the variable name (e.g. "local1") rather than the full
declaration ("val local1 = 2 + 3").

In K2 mode the IR offsets already point from the val/var keyword to the
end of the initializer, matching the intuitive source span.

Fix this for K1 by adding an IrVariable-specific overload of
getPsiBasedLocation that:
1. finds the leaf PSI element at v.startOffset (the name identifier)
2. walks up to the enclosing KtVariableDeclaration (KtProperty)
3. uses the val/var keyword position as the start offset to exclude
   any leading annotations from the span

This matches the K2 IR behavior and gives a more complete location for
variable declarations when used in CodeQL queries.

The fix applies to both K1 and K2 since the overload is unconditional;
for K2 the walk-up gives the same result as before.

Expected updates in test-kotlin1:
- variables: local variable locations now span from val/var to initializer
- exprs, stmts, methods, modifiers, reflection: same local variable span
- controlflow/basic: CFG node references updated for new variable spans
  (the "var ...;" node now starts at the val/var keyword)

Class member and top-level properties (IrProperty, not IrVariable) are
not affected by this change and retain their existing location behaviour.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-10 19:59:13 +02:00
Ian Lynagh
f48cc1a526 Kotlin: Move tests from test/kotlin to test-kotlin1
Matches test-kotlin2
2023-11-21 15:28:12 +00:00