When a constructor carries leading modifiers or annotations, for example
`internal constructor(...) { }` or an annotated `@Ann constructor()`,
the K1 frontend's IrBlockBody offsets for the constructor body begin at
the modifier/annotation (`3:3`); the K2 frontend begins at the
`constructor` keyword (`3:12`), consistent with how declarations exclude
leading modifiers from their own span. We converge K1 onto the K2 form.
The K2 span cannot be reconstructed from raw offsets under K1, but the
`constructor` keyword position is available from the PSI. A new helper
getPsiBasedConstructorBodyLocation walks from the block body's start to
the enclosing KtConstructor and returns a location from its
`constructor` keyword through the block body's own end offset;
extractBlockBody uses it in preference to the raw block location.
Guards keep the change surgical:
- returns null under K2 (getKtFile unavailable; raw offsets already
exclude the modifier), leaving K2 untouched.
- returns null for non-constructor bodies and for an implicit primary
constructor with no `constructor` keyword.
- for a constructor without modifiers the keyword coincides with the
block start, so the location is unchanged there.
Relearned both suites: only modifier/annotation-carrying constructor
body-block rows change (K1 now matches K2).
internal-constructor-called-from-java/test.expected becomes
byte-identical across suites; the residual annotation_classes/PrintAst
diffs are pre-existing, unrelated divergences (annotation-argument and
stdlib enum-entry locations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
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>
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>
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>