Skip to content

Fix spurious error on non-ident-headed type arguments#82

Merged
StreamDemon merged 1 commit into
mainfrom
fix/type-arg-error-pollution
Jul 2, 2026
Merged

Fix spurious error on non-ident-headed type arguments#82
StreamDemon merged 1 commit into
mainfrom
fix/type-arg-error-pollution

Conversation

@StreamDemon

@StreamDemon StreamDemon commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes Parser: spurious 'expected identifier' for non-ident-headed type args (Vec<&str>, tuples, arrays) #78: Vec<&str>, Vec<(i64, i64)>, Vec<[u8; 4]>, and Map<String, Vec<&str>> were rejected with a spurious "expected identifier" even though they are legal per §16 (type_arg = type | IDENT "=" type).
  • Root cause: the associated-type-binding probe in type_args_after_open called ident() and backtracked by resetting self.pos — but ident() pushes its failure diagnostic into self.errors first, and the checkpoint never truncated the error list. The fallback type parse succeeded while the stale error survived, so parse_program returned Err.
  • Fix: commit to the binding by peeking IDENT "=" before consuming anything — the same lookahead shape as turbofish and the send-statement head — so no failed probe ever runs. This also improves recovery for a genuinely bad binding value (Item = <garbage> now recovers to the next ,/> instead of re-parsing Item as a type argument).
  • This was the parser's only probe-and-backtrack site (self.pos = checkpoint appears nowhere else), so no other production has this failure mode.

Related Issue

Closes #78. Found during the enhancement wave (PR #71) while designing the attribute-argument parser; deliberately split out because it is a behavior change.

Spec Sections Affected

None — the spec and docs/reference/grammar.md already say these shapes are legal; this makes the parser match them.

Checklist

  • Code follows the Sploosh design principles (one way to do it, explicit over implicit, etc.)
  • Documentation updated in relevant docs/ pages — N/A; crates/AGENTS.md's accepted-subset list already claimed these shapes, the fix makes reality match it
  • Tests added or updated
  • All build targets still compile (if applicable)
  • Spec-only PR (skip Build Targets section if checked)

Build Targets Tested

  • cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace all green locally (56 tests, 14 corpus fixtures).

Test Plan

  • non_ident_headed_type_args_parse — the four probe cases from Parser: spurious 'expected identifier' for non-ident-headed type args (Vec<&str>, tuples, arrays) #78, plus a structural assertion that Vec<&str>'s argument survives as TypeArg::Type(Type::Reference { .. }) wrapping str.
  • assoc_binding_with_non_ident_headed_value_parsesIter<Item = Vec<&str>> keeps working through the new lookahead, asserted down to the TypeArg::Assoc shape.
  • New tests/corpus/generic_args.sp fixture covers the shapes end-to-end via the auto-discovering corpus harness.
  • Existing assoc-binding test (dyn_trait_type_args_with_assoc_binding) and the full suite pass unchanged.

Summary by cubic

Fixes a false "expected identifier" error when parsing generic type arguments that don’t start with an identifier. Uses IDENT '=' lookahead for associated-type bindings, so valid shapes like Vec<&str> now parse correctly (closes #78).

  • Bug Fixes
    • Replaced probe-and-backtrack in type_args_after_open with IDENT '=' lookahead to avoid stale diagnostics and improve recovery to the next comma or '>' on bad bindings.
    • Added tests for non-ident-headed type args and assoc bindings with such values, plus a new corpus fixture tests/corpus/generic_args.sp.

Written for commit fcc29f0. Summary will update on new commits.

Review in cubic

`type_args_after_open` probed for an associated-type binding by calling
`ident()` and backtracking on failure — but `ident()` records an
"expected identifier" diagnostic before the backtrack, and the
checkpoint only rewound the position, never the error list. The
fallback type parse then succeeded while the stale error survived, so
spec-legal arguments like `Vec<&str>`, `Vec<(i64, i64)>`, and
`Vec<[u8; 4]>` were rejected.

The binding now commits by peeking `IDENT "="` before consuming
anything, the same shape as the parser's other lookaheads (turbofish,
send-statement head), so no failed probe ever runs. A genuinely bad
binding value (`Item = <garbage>`) now recovers to the next comma or
`>` instead of re-parsing the binding name as a type argument.

Closes #78. The probe cases from the issue land as regression tests,
plus a corpus fixture for non-ident-headed generic arguments.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Input as Token Stream
    participant Parser as type_args_after_open()
    participant Errors as Error List
    participant Output as Vec<TypeArg>

    Note over Parser: Loop over type arguments until '>' or EOF
    loop for each type arg
        Parser->>Input: peek current token
        Input-->>Parser: TokenKind
        Parser->>Input: peek next token (peek_kind_at(1))
        Input-->>Parser: TokenKind or None

        alt IDENT followed by '=' (NEW lookahead – no probe)
            Parser->>Input: consume IDENT
            Parser->>Input: consume '='
            Parser->>Input: parse type()
            Input-->>Parser: Ty
            Parser->>Parser: push TypeArg::Assoc { name, ty }
        else else – direct type parse
            Parser->>Input: parse type()
            alt type parsed successfully
                Input-->>Parser: Ty
                Parser->>Parser: push TypeArg::Type(ty)
            else type parse fails
                Parser->>Parser: recover_until(Comma, Gt)
                Note over Parser: No stale error from abandoned ident() probe
            end
        end

        Parser->>Input: try consume comma
        alt comma consumed
            Parser->>Parser: continue loop
        else no comma
            Parser->>Parser: break
        end
    end

    Parser-->>Output: list of TypeArg
Loading

Auto-approved: Fix #78: replace probe-and-backtrack with lookahead in type_args_after_open to avoid stale errors on non-ident-headed type args like Vec<&str>. Adds tests.

Re-trigger cubic

@StreamDemon StreamDemon merged commit 91a7e78 into main Jul 2, 2026
3 checks passed
@StreamDemon StreamDemon deleted the fix/type-arg-error-pollution branch July 2, 2026 12:18
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.

Parser: spurious 'expected identifier' for non-ident-headed type args (Vec<&str>, tuples, arrays)

1 participant