A source underscore variable (an unused `_` in a `catch (_: E)` clause or a
`val _ = ...` discard) is named differently by the two frontends:
- K1 keeps the source spelling `_`.
- K2 assigns the synthetic `SpecialNames.UNDERSCORE_FOR_UNUSED_VAR`, which
renders as `<unused var>`.
Decision (D15): adopt the K1 behaviour. `_` is the actual source token, so it
is the more intuitive and source-faithful name; it also keeps the local
variable name consistent with the corresponding value-parameter case, where a
prior fix already normalises the underscore setter parameter to `_`.
`extractVariableExpr` now maps a variable whose IR name is
`SpecialNames.UNDERSCORE_FOR_UNUSED_VAR` to `_` before writing `localvars`.
The check is on the special name rather than a raw string so it is robust
across compiler versions, and it only fires for the frontend-synthesised
unused-variable name, leaving all other locals untouched.
Full dual-suite relearn: all 3333 tests pass. The only changed expected row is
in query-tests/UnderscoreIdentifier, where the K2 catch parameter row converges
from `Exception <unused var>` to `Exception _`, matching K1. The remaining
divergence in that file (the destructuring container `<destruct>` vs
`tmp0_container`) is a separate naming/location issue tracked under C14.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A data class's generated `copy(...)` has one value parameter per
primary-constructor property. The K1 frontend records each such
parameter (and its type accesses) at the source location of the
corresponding property; the K2 frontend leaves them with undefined
offsets, which the extractor emits as a `0:0:0:0` location.
This divergence is purely a K2 information regression: the richer K1
location is unambiguously better (it points at the real property in
source, enabling location-based queries), so we converge K2 onto K1
rather than the other way around.
Because K2 exposes no PSI back-mapping, the location cannot be
recomputed from source; instead we recover it from the IR. For a value
parameter of a `GENERATED_DATA_CLASS_MEMBER` function whose own offsets
are undefined, we look up the primary-constructor parameter at the same
index and reuse its location.
Guards keep the change surgical:
- `vp.startOffset >= 0` bails out, so K1 (which already has real
offsets) is untouched.
- the origin must be `GENERATED_DATA_CLASS_MEMBER`.
- the primary-ctor parameter name must match and carry real offsets,
which restricts the remap to `copy`-style parameters and excludes
members such as `equals(other)`.
Relearned both suites: only data-class `copy` parameter rows change
(K2 now matches K1). data-classes/PrintAst.expected becomes byte
-identical across suites; the residual diffs in methods/{exprs,
parameters}.expected are pre-existing, unrelated divergences.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy K1's richer C2.kt (which includes test2, test3 and l.get(0) in test)
into test-kotlin2. K2 in -language 2.0 mode already tracks dataflow through
array.set() and indirect wrapper calls, so the additional tests produce results
identical to K1.
The expected files for test-kotlin1 and test-kotlin2 are now byte-identical for
this test.
Verified:
- test-kotlin1 (kotlinc 2.3.20 / -language 1.9): all tests pass
- test-kotlin2 (kotlinc 2.4.0 / -language 2.0): all tests pass
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In K2 mode the frontend emits `-123L` as IrCall(unaryMinus, IrConst(123L))
rather than IrConst(-123L) as in K1. Queries that search for negative numeric
literals therefore need to match both a UnaryMinusExpr wrapping a literal and a
plain literal, depending on language mode.
Fix: when extractCallExpression encounters an isNumericFunction(unaryMinus) call
whose dispatchReceiver is already an IrConst, fold the negation into the constant
before extracting. The resulting literal node is identical to what K1 emits.
Location: extend the span one character to the left to cover the `-` sign.
In K2 the IrCall's startOffset equals the receiver's startOffset, so we recover
the minus by subtracting one from the receiver offset.
K1 is unaffected: the K1 frontend folds the sign into the constant before IR
generation, so this new branch never triggers when compiling with -language 1.9.
Expected output changes:
- test-kotlin2/library-tests/literals/literals.expected: negative long, float and
double literals now appear as plain typed literals instead of as UnaryMinus nodes.
The file is now byte-identical to test-kotlin1/library-tests/literals/literals.expected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Port ministdlib from test-kotlin1 to test-kotlin2. The ministdlib test
exercises a minimal Kotlin standard library written from scratch. Its
options file is updated to include -language-version 2.0 so the test
runs in K2 mode when the K2 compiler is active.
- Port nested_types from test-kotlin2 to test-kotlin1. The nested_types
test exercises type-alias and inner-type queries. Expected output is
identical in K1 and K2 modes so no expected-file changes are needed.
- Add test-kotlin2/options with codeql-extractor-kotlin-options:
-language-version 2.0. The CodeQL CLI adds -language-version 1.9 by
default in legacy test extraction mode. Without this override the K2
test suite would run in K1 mode, defeating the purpose of the split.
Both ministdlib and nested_types produce byte-identical expected output
across K1 (2.3.20, -language-version 1.9) and K2 (2.4.0, default K2).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Accept test changes from Kotlin 2.3.0 update
Updates expected test outputs for kotlin2 library tests to match
actual compiler output. Changes include:
- Location adjustments for properties/methods (now point to identifiers)
- CastExpr -> ImplicitCastExpr for implicit type casts
- Removed duplicate BlockStmt entries in loop ASTs
- Super constructor call location changes
Note that in Kotlin 2.3.0 super constructor calls now have locations spanning
entire class declarations instead of the actual super call site.
These are mainly small changes in how source-locations are ascribed to synthetic expressions, plus three real changes:
- The comment extractor is performing better presumably due to improvements in the underlying representation
- *= /= and %= operations are once again extracted correctly; presumably their origin information has been fixed
- Reference to a static final Java field can lead to more constant propagation than before
The last one might be a minor nuisance to someone trying to find references to such a field.
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.