tree-sitter: fix type<T>; parse failure in type position#2877
Merged
Conversation
`type<T>` is a valid type in daslang (canonical witness-parameter form, e.g. `t : type<auto(T)>` in clargs.das and elsewhere). The tree-sitter grammar only recognized it inside `_type_macro_arg` (the inner sequence of a `padded(type<T>, ...)` macro call) and as `type_expression` (which appears in expression positions only). Function parameters, return types, and struct fields declared with `type<T>` therefore fragmented the parse -- breaking syntax highlighting in Zed and any other editor using this grammar. Add a dedicated `type_witness` rule mirroring bison's `DAS_TYPE '<' type_declaration '>'` production (ds2_parser.ypp:3281) and add it to `_type`. Reuse from `_type_macro_arg`. The existing `type_expression` rule stays -- it remains the expression-context form (e.g. `let x = type<int>`), and the conflict declaration is updated to `[type_expression, type_witness]`. Five new corpus tests cover: basic param, auto generic param (the bug report case), return type, struct field, nested. All 34 corpus tests pass; the previous 29 are unchanged. parser.c regen also reflects a CLI version bump (the 0.22.0 cli no longer runs on Node 24, so generation used 0.26.9 with ABI 14). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a tree-sitter parsing gap where type<...> (type-witness form) was only recognized in limited contexts, causing broken parses/highlighting when used in type positions like function parameters, return types, and struct fields.
Changes:
- Introduces a dedicated
type_witnessrule and includes it in_typesotype<T>is accepted anywhere a type is expected. - Reuses
type_witnessinside_type_macro_argand updates the declared conflict to[type_expression, type_witness]to preserve expression-contexttype<...>parsing. - Adds new corpus coverage for type-witness usage in parameter, return type, struct field, and nested generic-type cases.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tree-sitter-daslang/grammar.js | Adds type_witness, wires it into _type and _type_macro_arg, and updates the conflict set vs type_expression. |
| tree-sitter-daslang/src/grammar.json | Regenerates the compiled grammar to include type_witness and the updated conflict declaration. |
| tree-sitter-daslang/src/node-types.json | Regenerates node metadata to add the type_witness node and adjust affected parent/child relationships. |
| tree-sitter-daslang/test/corpus/declarations.txt | Adds corpus cases verifying type<...> parses correctly in multiple type positions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reported via Telegram (user Vi): Zed syntax highlighting breaks on
type<…>used as a function parameter type, e.g.Root cause: the tree-sitter grammar only recognized
type<T>in two places — inside_type_macro_arg(the inner sequence of apadded(type<T>, …)macro call) and astype_expression(which appears in expression positions only). Function parameters, return types, and struct fields declared withtype<T>therefore fragmented the parse — even though this is the canonical witness-parameter form used throughout daslib (e.g.daslib/clargs.daslines 239/246/263/278/289).Bison reference:
type_declaration_no_options_no_dim → DAS_TYPE '<' type_declaration '>'(ds2_parser.ypp:3281).Changes
type_witnessrule mirroring the bison production, added to_typeso it's accepted wherever a type is expected (parameters, return types, struct fields, typedefs, generic args, etc.)._type_macro_argnow reusestype_witness.type_expressionstays — it remains the expression-context form (let x = type<int>); the conflict declaration is updated to[type_expression, type_witness]."type" @keywordin bothqueries/highlights.scmandzed-daslang/languages/daslang/highlights.scmalready covers the keyword highlight globally; no query changes needed.Tests
Five new corpus cases added to
test/corpus/declarations.txt:def parse_int(t : type<int>)def get_type_id(t : type<auto(T)>) : int(the bug-report case)def make_witness() : type<int>struct Foo { t : type<float> }def foo(t : type<array<int>>)tree-sitter testresult: 34/34 pass (5 new + 29 prior, no regressions).Notes on parser.c regen
The 0.22.0 CLI pinned in
package.jsondoesn't run on Node 24 (EISDIR on drive root during grammar.js module resolution), so generation used 0.26.9 (still ABI 14). The parser.c diff is large because of the CLI version bump on top of the rule addition;grammar.json(+52/-26) andnode-types.json(+22/-2) reflect only the rule change.Test plan
tree-sitter test(all 34 corpus cases pass)tree-sitter parseon the bug-report snippet produces(type_witness type: (auto_type (identifier)))correctly nested underfunction_argumenttree-sitter parseonpadded(type<int>, 16)in type position still produces(type_macro …)(existing path intact)tree-sitter parseonlet x = type<int>still produces(type_expression …)(expression path intact)🤖 Generated with Claude Code