Skip to content

feat(parser): support ES6-style shorthand entries in config/schema literals (fixes #1275) - #2163

Merged
Peefy merged 3 commits into
mainfrom
worktree-feat/issue-1275-shorthand
Aug 31, 2026
Merged

feat(parser): support ES6-style shorthand entries in config/schema literals (fixes #1275)#2163
Peefy merged 3 commits into
mainfrom
worktree-feat/issue-1275-shorthand

Conversation

@Peefy

@Peefy Peefy commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds ES6-style shorthand for config/schema literals (issue #1275). A bare
identifier inside { ... } now stands for key = key:

schema Person:
    name: str
    age: int

name = "next"
age  = 30

p = Person { name, age }          # === Person { name = name, age = age }
m = { name, age }                 # === { name = name, age = age }
n = Person { name, age = 99 }     # mixed shorthand + override

This is purely additive: every existing entry carries an explicit
separator, so any program that parses today still parses tomorrow. The
new arm fills the parser-error gap that appeared when the separator was
missing.

Implementation

  • AST (crates/ast/src/ast.rs): new is_shorthand: bool field on
    ConfigEntry with #[serde(default, skip_serializing_if = "is_false")]
    so existing AST-JSON fixtures continue to deserialize.
  • Parser (crates/parser/src/parser/expr.rs): parse_config_entry
    now reuses the parsed identifier as both key and value when no
    separator follows a single-segment Identifier key. Dotted keys
    like { a.b } still hit the existing "expected one of :/=/+="
    diagnostic so ConfigNestAttrTransformer key-flattening keeps its
    current semantics.
  • Formatter (crates/ast_pretty/src/node.rs): write_entry short-
    circuits on is_shorthand to round-trip {name} exactly. We never
    rewrite explicit name = name into shorthand (avoids silently
    flipping semantics inside schema bodies where name could resolve
    to a local).
  • Struct-literal call sites: is_shorthand: false added in
    crates/parser/src/parser/expr.rs (3 sites in parse_body_item),
    crates/sema/src/pre_process/config.rs,
    crates/query/src/override.rs (3), and
    crates/tools/src/vet/expr_builder.rs (4).

No evaluator, walker, sema transformer, or loader changes needed —
existing consumers walk key + value and ignore the new flag.

Tests

  • 4 new parser snapshot tests (config_shorthand_stmt_0..3) covering
    the all-shorthand, mixed, and explicit-baseline cases.
  • Existing config_recovery_0..15 snapshots regenerated to include the
    new field (no behaviour change; just the additive is_shorthand: false).
  • 3 new grammar tests under tests/grammar/schema/:
    • shorthand/ — positive case
    • shorthand_dotted_fail/ — dotted-key rejection
    • shorthand_format/ — formatter round-trip
  • cargo test -p kcl-parser → 514 passed, 0 failed
  • cargo test -p kcl-tools --lib → 60 passed, 0 failed
  • cargo test -p kcl-query --lib → 15 passed, 0 failed
  • cargo test -p kcl-sema --lib → 61 passed, 0 failed

Out of scope (follow-ups)

  • Positional / type-based shorthand (DiskOptions { 32, "ssd" }).
  • Shorthand for dotted keys ({a.b}).
  • Canonicalizing explicit name = name{name} via kcl fmt.

🤖 Generated with Claude Code

…terals (fixes #1275)

A bare identifier inside a config or schema-instance literal is now treated
as shorthand for `key = key`. For example, `{ name, age }` parses to two
ConfigEntry nodes with `is_shorthand = true`, equivalent to
`{ name = name, age = age }`. Mixed forms like `Person { name, age = 99 }`
are accepted. Dotted keys like `{ a.b }` are still rejected via the
existing "expected `:`/`=`/`+=`" diagnostic so key-flattening keeps its
current semantics.

- AST: new `is_shorthand: bool` field on `ConfigEntry` with
  `#[serde(default, skip_serializing_if = "is_false")]` so existing
  AST-JSON fixtures keep deserializing.
- Parser: `parse_config_entry` now reuses the parsed identifier as both
  key and value when no separator follows a single-segment `Identifier`
  key.
- Formatter: `write_entry` short-circuits on `is_shorthand` to round-trip
  `{name}` exactly; we never rewrite explicit `name = name` into shorthand.
- Struct-literal call sites updated to set `is_shorthand: false`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
@Peefy
Peefy force-pushed the worktree-feat/issue-1275-shorthand branch from 5959258 to 98c79d0 Compare August 30, 2026 13:44
Peefy added 2 commits August 31, 2026 10:40
The shorthand test main.k declares top-level name/age variables and three
schema/config instances (p, m, n). libkcl emits all top-level variables
in order, including the source of the shorthand. Single-letter 'n' is
quoted in YAML output (consistent with KCL's standard dump).

Verified locally against /target/release/libkcl: all 1603 grammar tests
pass, including schema/shorthand, schema/shorthand_dotted_fail, and
schema/shorthand_format.

Signed-off-by: Peefy <xpf6677@163.com>
The shorthand parser change makes the previous test data file — which
relied on parser errors to leave each entry incomplete — now parse as
seven valid shorthand entries (`a = a`, `b = b`, etc.). The value side
of each entry resolves to an undefined variable, so the resolver never
registers the attribute on the schema, breaking LSP completion.

Switch the test data to `a:` style separators so each entry stays
incomplete (`a: <cursor>`) just like a user typing in the IDE. This
matches the LSP's actual use case without depending on parser-error
recovery.

Signed-off-by: Peefy <xpf6677@163.com>
@Peefy
Peefy merged commit e7d30c4 into main Aug 31, 2026
10 checks passed
@Peefy
Peefy deleted the worktree-feat/issue-1275-shorthand branch August 31, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants