feat: H1a CROSS JOIN ON teaching message; advanced-SQL gaps re-verified (ADR-0042)
Empirically re-checking ADR §3's advanced-SQL "gaps" reversed two of three — the code survey that produced the list was wrong: - INSERT…SELECT column-count: already handled (verdict=Error, "the column list names N column(s) but M value(s) are given"; insert_select_arity_mismatch_fires). - RETURNING scope: already handled (completion offers the table's columns; `returning <unknown>` → unknown_column diagnostic). The one genuine residual is fixed: `select … cross join b on …` rejected the ON with a bare "expected end of input". Add parse.cross_join_no_on — "a CROSS JOIN has no ON clause — it pairs every row; for a join condition use `JOIN … ON`, or filter with `WHERE`" — rendered when the failing token is `on` and the most recent consumed join is a CROSS join (a precise signature: every other join requires `on`, so `on` is expected there, not a failure). Render-only in format_walker_error; two misfire guards locked (plain join still asks for ON; a stray `on` with no join does not fire). ADR-0042 §3 corrected + Implementation-outcome records the advanced-SQL re-check and the user-confirmed low-priority residual (submit-time expression first-set at non-projection positions, where typing-time completion already offers the right candidates). Full suite green (lib 1578 / it 388 / typing_surface_matrix 192); clippy clean.
This commit is contained in:
@@ -255,6 +255,37 @@ fn advanced_mode_usage_block_shows_sql_and_dsl_forms() {
|
||||
assert!(sql_at < dsl_at, "SQL form should precede the DSL form\n{dump_msg}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advanced_cross_join_with_on_teaches_no_on_clause() {
|
||||
// ADR-0042 §3: a CROSS JOIN has no ON clause. The grammar
|
||||
// rejects a following `on`, but the bare structural error
|
||||
// ("expected end of input") does not teach why. `on` is
|
||||
// unexpected here only because the most recent join is a CROSS
|
||||
// join — every other join flavour *requires* `on` — so the case
|
||||
// is precisely detectable and gets a teaching message.
|
||||
let input = "select * from a cross join b on x = y";
|
||||
let lines = advanced_error_lines_for(input);
|
||||
let joined = lines.join("\n");
|
||||
let dump_msg = dump(input, &lines);
|
||||
assert!(
|
||||
joined.contains("a CROSS JOIN has no ON clause"),
|
||||
"cross join + on should teach that CROSS JOIN takes no ON\n{dump_msg}",
|
||||
);
|
||||
// Misfire guard 1: a plain JOIN missing its ON still asks for `on`.
|
||||
let plain = advanced_error_lines_for("select * from a join b").join("\n");
|
||||
assert!(
|
||||
plain.contains("expected `on`") && !plain.contains("CROSS JOIN"),
|
||||
"a plain join must still ask for ON, not the cross-join message: {plain}",
|
||||
);
|
||||
// Misfire guard 2: a stray `on` with no join present must NOT
|
||||
// claim a CROSS JOIN.
|
||||
let stray = advanced_error_lines_for("select * from a on x = y").join("\n");
|
||||
assert!(
|
||||
!stray.contains("CROSS JOIN has no ON"),
|
||||
"no cross join present — must not fire: {stray}",
|
||||
);
|
||||
}
|
||||
|
||||
/// The advanced-mode near-miss matrix (ADR-0042 §1/§3). Mirrors
|
||||
/// the simple-mode matrix for the SQL surface. Every row must show
|
||||
/// a per-command `usage:` block (never the available-commands
|
||||
|
||||
Reference in New Issue
Block a user