feat: H1a parse-error gaps G2–G4 + advanced near-miss matrix (ADR-0042)

Close the three remaining ADR-0042 triage gaps, each test-first, and
lock the advanced-mode near-miss matrix.

G2 — bare `select` dumped the 14-item expression first-set. Collapse
it to "a projection: `*`, a column, or an expression" in the error
message only (parser::format_walker_error), detected by the joint
`distinct`+`all` quantifier signature unique to a projection start.
Render-only: completion/hints still expand the full set (typing-surface
matrix unchanged).

G3 — the usage block was mode-blind: advanced `create table` showed the
DSL `create table … with pk …` template. usage_key(s)_for_input gain
mode-aware `_in_mode` variants selecting candidates by CommandCategory;
render_usage_block and the typing-time ambient usage thread the
submission mode. Advanced `create` now shows both SQL forms. A fallback
covers shared SQL nodes (insert/update/delete) that declare no
usage_ids of their own — without it they regressed to the
available-commands fallback (caught by the new advanced matrix).

G4 — `with` borrowed `select`'s usage template; give it its own
parse.usage.with CTE template.

Tests: new near_miss_matrix_advanced_mode (12 SQL-surface cases incl.
the available-commands regression guard) + per-gap tests; removed the
temporary baseline_dump. Full suite green (lib 1578 / it 386 /
typing_surface_matrix 192); clippy clean.
This commit is contained in:
claude@clouddev1
2026-06-05 14:57:20 +00:00
parent 10f8c2a95c
commit 649fdcb38e
8 changed files with 259 additions and 93 deletions
+25 -2
View File
@@ -296,14 +296,37 @@ fn format_expectation(e: &crate::dsl::walker::outcome::Expectation) -> String {
}
}
/// ADR-0042 G2: a projection start (`select |`, or the projection
/// position inside a subquery / CTE body) expects the full
/// expression first-set — 14 alternatives — plus the SELECT
/// quantifiers `distinct` and `all`. Those two quantifiers are
/// jointly expectable *only* at a projection start, so their joint
/// presence is a precise signature for collapsing the noisy list
/// into one gloss. Render-only: this fires inside
/// `format_walker_error` (the error message), not in the expected
/// set the completion/hint layer consumes.
fn is_select_projection_start(expected: &[crate::dsl::walker::outcome::Expectation]) -> bool {
use crate::dsl::walker::outcome::Expectation;
let has_word = |w: &str| {
expected
.iter()
.any(|e| matches!(e, Expectation::Word(x) if x.eq_ignore_ascii_case(w)))
};
has_word("distinct") && has_word("all")
}
fn format_walker_error(
source: &str,
position: usize,
at_eof: bool,
expected: &[crate::dsl::walker::outcome::Expectation],
) -> String {
let parts: Vec<String> = expected.iter().map(format_expectation).collect();
let joined = oxford_join(&parts);
let joined = if is_select_projection_start(expected) {
crate::t!("parse.expect.select_projection")
} else {
let parts: Vec<String> = expected.iter().map(format_expectation).collect();
oxford_join(&parts)
};
// Mirror the chumsky-side wording: "after `<consumed>`,
// expected …" when the parser already consumed something