Commit Graph

9 Commits

Author SHA1 Message Date
Anders Fugmann
a55f7caab5 Kotlin: span the whole class for primary-ctor synthetic super calls
A primary constructor's call to its superclass is written in source only
as a supertype-list entry (for example `class C : Base()`), so the
synthetic delegating super-constructor call has no `super(...)` token of
its own. The K1 frontend records it at that supertype call expression
(`12:23:12:34`); the K2 frontend records it at the whole class
declaration (`12:1:15:1`).

Neither is a real `super(...)` statement. Consistent with the location
policy adopted for these synthetic constructs (prefer the fuller
whole-construct span), we converge K1 onto the K2 form.

Because the whole-class span cannot be reconstructed under K2 (no PSI
back-mapping) but K2 already emits it from raw IR, the fix is K1-only: a
new helper getPsiBasedPrimaryCtorSuperCallLocation returns the enclosing
KtClassOrObject span (including leading modifiers such as `open`) for the
super call of a primary constructor, and the IrDelegatingConstructorCall
handler uses it for super calls (delegatingClass != currentClass).

Guards keep the change surgical:
  - applies to primary constructors only (both explicit `()` and fully
    implicit); an explicit `super(...)` in a secondary constructor keeps
    its own location, which both frontends already record identically.
  - returns null under K2 (getKtFile unavailable; raw offsets already
    carry the class span), leaving K2 untouched.

Relearned both suites: only primary-ctor super-call rows change (K1 now
matches K2). classes/ctorCalls.expected becomes byte-identical across
suites; the residual vararg/args diff is a pre-existing, unrelated
`public vararg val` parameter-span divergence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-12 16:57:47 +02:00
Anders Fugmann
8db987e4ea Converge if/when branch (WhenBranch) locations onto K2 per-branch spans
The K1 frontend lowers `if`/`when` branches with their raw IR offsets
collapsed onto the whole enclosing IrWhen expression, so every branch
reports the same span (e.g. `stmts.kt:17:26:17:58`). K2 records per-branch
spans reconstructed from the branch condition start through the result end
(or just the result span for an `else` branch).

Reconstruct the per-branch span from the branch's own condition/result
offsets, gated on the raw branch span being collapsed onto the enclosing
IrWhen (as under K1); under K2 the raw per-branch offsets already differ so
the helper is a no-op. `correctedEndOffset` additionally fixes K1 recording
a bare assignment's raw end at its left-hand side rather than past its
right-hand value.

Example (stmts.kt:17):
  before: 17:26:17:58 (x2, both branches collapsed onto the IrWhen)
  after:  17:29:17:43 and 17:50:17:58 (per-branch, matching K2)

Only the K1 (test-kotlin1) expected files change; the K2 (test-kotlin2)
expected files are unchanged. Controlflow expected files converge as a pure
cascade of the branch-location shift.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-11 15:07:43 +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
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
bb17e31801 kotlin-extractor: PSI-backed source locations for expression-level nodes
K1 and K2 IR backends compute source locations differently. K1 uses the IR
node's synthetic startOffset/endOffset, while K2 reconstructs source positions
from PSI. For expression-level nodes this causes location differences across
the two language modes.

Introduce PSI-backed location lookup as the preferred source for spans wherever
PSI is available:

  getPsiBasedLocation(element) ?: tw.getLocation(element)

getPsiBasedLocation() resolves the PSI element for the IR node via
psi2Ir.findPsiElement() and builds a location from its startOffset..endOffset.
currentIrFile is tracked in extractFileContents so the PSI lookup has the
file context it needs.

Applied to expression-level nodes (both K1 and K2 modes):
- local variable declarations (extractVariable, extractVariableExpr)
- IrLocalDelegatedProperty blocks
- IrWhen expressions and when-branches
- IrGetValue (varaccess) expressions
- IrFunctionExpression (lambda) nodes
- Block statements (extractBlock)
- this/super access expressions (extractThisAccess)
- String literals

Declaration-level nodes (class, function, property) are guarded with
if (usesK2) to avoid a regression in K1 mode where the PSI lookup causes
parameterised type instantiations to appear as fromSource(), inflating
generic-type query results. The K1 IR frontend does not map all declaration
nodes cleanly to source PSI elements; for these nodes we keep the original
IR-based location in K1 mode.

Expected output changes (both suites):
- controlflow/basic/bbStmts, bbStrictDominance, bbSuccessor, getASuccessor,
  strictDominance: when-branch and varaccess location improvements
- java-kotlin-collection-type-generic-methods/test: new stdlib entries from
  JDK update (AbstractCollection<Runnable> methods)
- annotation_classes/PrintAst: variable access location improvement in K1
- classes/genericExprTypes: location improvement in K1
- compilation-units/cus: removed two internal JDK inner-class entries (stdlib
  version change)
- reflection/reflection: removed a few external-class entries (stdlib version)

Verified: all 285 tests pass for both test-kotlin1 (kotlinc 2.3.20 / K1) and
test-kotlin2 (kotlinc 2.4.0 / K2).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-10 19:58:41 +02:00
Chris Smowton
5d3f723df9 Kotlin extractor: use special <nulltype> for null literals
This matches the Java extractor's treatment of these literals, and so enables dataflow type-tracking to avoid special-casing Kotlin. Natively, Kotlin would regard this as kotlin.Nothing?, the type that can only contain null (kotlin.Nothing without a ? can take nothing at all), which gets Java-ified as java.lang.Void, and this will continue to be used when a null type has to be "boxed", as in representing substituted generic constraints with no possible type.
2024-11-01 16:14:10 +00:00
Tom Hvitved
bf0675e5ba Kotlin: Update two tests 2024-10-04 08:35:30 +02:00
Ian Lynagh
918bee07dd Kotlin: Accept Kotlin 1 test changes 1.9.0 -> 2.0.0 2024-06-03 15:26:51 +01:00
Ian Lynagh
f48cc1a526 Kotlin: Move tests from test/kotlin to test-kotlin1
Matches test-kotlin2
2023-11-21 15:28:12 +00:00