Parser improvements: empty record, dot-shorthand patterns, abstract/external fields, null-aware map keys, etc.#97
Merged
TimWhiting merged 17 commits intoMay 5, 2026
Conversation
Per the Dart specification, a record literal may have zero positional and zero named fields, written as `()` (the unit/empty record). The existing `_record_literal_no_const` rule required at least one field, so `()` failed to parse as an expression in real-world code. Reference: Dart Language Specification, Records (recordLiteral) — an empty `()` is a record with zero positional fields and zero named fields. The empty form is given a negative dynamic precedence so that, in contexts like `f<T>()` where the parser must choose between type_arguments + empty arguments and a relational expression with an empty record on the right, it prefers the existing call/type-argument path. Conflicts added between `_record_literal_no_const` and `record_type` / `_strict_formal_parameter_list` to keep the GLR parser exploring all three interpretations of `(...)` in their respective contexts.
Per Dart 3.10 dot-shorthand and the patterns specification, a
dot-shorthand expression may appear as a constant pattern, e.g.
switch (c) {
case .red: // static member dot-shorthand
case .new(): // default constructor dot-shorthand
case .fromRGB(1): // named constructor dot-shorthand with args
}
and `const` may prefix a dot-shorthand constructor invocation, with the
receiver type inferred from context:
const Color c = const .new();
const Color d = const .fromRGB(1, 2, 3);
Adds:
- `$.dot_shorthand` (optionally followed by type arguments and an
arguments list) as a `constant_pattern` choice.
- `seq($.const_builtin, $.dot_shorthand, $.arguments)` as a second
alternative in `const_object_expression`.
Reference: Dart Language Specification — Dot Shorthands and Patterns.
Per the Dart specification, `get` and `set` are built-in identifiers
(not reserved words) and may be used as ordinary identifiers — for
example as the name of a local variable or a class field with an
explicit type. The `_declared_identifier` rule already aliases
`_get`/`_set` to `identifier`; mirror that on `initialized_identifier`
so declarations like
class C {
int set = 0;
String get = 'foo';
}
parse correctly. Without this change, the grammar fell back to the
getter/setter declaration rules (which require an initializer-less
signature) and failed in real-world packages such as `sqlparser` and
`s_packages` that use `set`/`get` as field names.
Adds GLR conflicts so the parser keeps the variable-declaration and
getter/setter signature paths open while reading `Type get` /
`Type set`.
Reference: Dart Language Specification — Built-in Identifiers
(`get`, `set`) and Variable Declaration / initializedIdentifier.
Per the Dart specification, `get`, `set`, and `Function` are built-in identifiers and may appear as ordinary identifiers — including on the left-hand side of an assignment, or as the receiver of a method call. Real-world packages such as `sqlparser` (`set = ...`) and `auro_stream_live` (`Function.apply(...)`) failed to parse because: - `assignable_expression` only accepted `$.identifier` for the bare-identifier case, so `set = expr` and `get = expr` parsed as the start of a setter/getter signature instead of an assignment. - `_primary` did not include `Function` as an identifier, so `Function.apply(args)` could not be parsed as a method call (the `Function` class is referenced by name). Adds aliased `_get`, `_set`, and `_function_builtin_identifier` choices to both `_primary` and `assignable_expression`, plus the required GLR conflicts (`_primary` vs. `_type_not_void_not_function` / `_function_type_tail`) so the parser can still recognize `Function` as part of a function type (`Function(int)`, `Function?`). Reference: Dart Language Specification — Built-in Identifiers (`get`, `set`, `Function`).
Two related Dart 3 declaration forms were missing:
1. Abstract instance fields. Per the Dart spec, an instance field
inside an abstract class may be declared without an initializer
using the `abstract` modifier; the field has no implementation and
subclasses must provide a concrete getter/setter. Forms supported:
abstract <type>? <ids>;
abstract final <type>? <ids>;
abstract covariant <type>? <ids>;
Affects packages such as `embrace`, `crypto_wallet_util`, and
`catalyst_builder`.
2. Top-level external variable declarations. Used by `dart:js_interop`
patterns such as `@JS() external _DdLogs? DD_LOGS;`. The existing
`_top_level_definition` choice supported `external function/getter/
setter` signatures but not external variables. Forms supported:
external <type>? <ids>;
external final <type>? <ids>;
external late <type>? <ids>;
Affects packages such as `datadog_flutter_plugin`, `dwds`, and
`ditto_live`.
Reference: Dart Language Specification — Abstract Instance Variables
and (Static) External Variables.
Two more built-in identifier sites the grammar did not yet treat as ordinary identifiers: - `label` only accepted `\$.identifier`, so named arguments like `f(get: x, set: y)` failed to parse. Real-world example: `datahub_aperture` constructs `ResourceEndpoint(get: ..., post: ...)`. - `initialized_identifier` and `_declared_identifier` did not accept `operator` as an identifier, so `String operator;` did not parse. Real-world example: `ffastdb` declares a `String operator;` field. Adds aliased `_get`, `_set`, `_function_builtin_identifier` choices to `label`, and aliased `_operator` to both `initialized_identifier` and `_declared_identifier`. Also adds a GLR conflict between `operator_signature` and `_var_or_type` so the parser keeps both the operator-method-signature path (`Type operator +(...)`) and the variable-declaration path (`Type operator;`) open. Reference: Dart Language Specification — Built-in Identifiers (`get`, `set`, `operator`, `Function`).
Per the Dart specification, a single `;` is the empty (null) statement. It can appear anywhere a statement is expected, e.g. as the body of an `if (cond) ;` form, after a `return;` continuation, or as a stray `;` in a function body. The grammar previously had no rule for this, so real-world code using it failed to parse. Adds an `empty_statement` rule and includes it in `_statement`. Affects packages such as `crypto_wallet_util` (`if (!isUint(n, 53)) ;`) and `littlefish_feature_user_pins` (`return; ;`). Reference: Dart Language Specification — Statements (emptyStatement).
Per the Dart specification (Static Interop / External Members), a
class instance field may be declared with the `external` modifier in
combination with `final` or `covariant`. The grammar previously
covered only the bare `external <type> <id>` form (via the
`_external_and_static + _type + identifier` rule), so
extension type _Foo._(JSObject _) implements JSObject {
external final JSString field;
}
failed to parse. Affects `dart:js_interop` extension types in
packages such as `ditto_live`.
Adds two new declaration alternatives:
external final <type>? <id-list>
external covariant <var-or-type> <id-list>
Per the Dart spec, expression lists generally allow trailing commas.
The C-style for-loop's update list (`for (init; cond; u1, u2,)`) was
parsed with `commaSep` (no trailing comma); switch to
`commaSepTrailingComma` so code such as
for (var i = 0; i < n; i += 15,) { ... }
parses. Affects packages such as `pdf_plus`.
Per Dart spec the right-hand side of a constructor field initializer
is a full `expression`, including compound assignments such as `??=`.
The grammar restricted it to `_real_expression`, which excluded
assignment expressions, so code such as
A(int? x) : x = x ??= 5;
failed to parse. Real-world examples in `matrix` (constructor body
chained `??=` assignments) and `syncfusion_flutter_gauges`.
Reference: Dart Language Specification — Constructor field
initializers (`fieldInitializer`).
Per Dart 3 (null-aware elements), a map entry may use `?` before the
key as well as before the value to mark the corresponding sub-
expression as null-aware:
{
?maybeKey: 1, // omit entry if maybeKey is null
key: ?maybeValue, // omit entry if maybeValue is null
?maybeKey: ?maybeValue,
}
The grammar previously allowed `?` only before the value. Adds
`optional('?')` before the key in the `pair` rule. Affects packages
such as `analyzer` and `drift_dev`.
Reference: Dart Language Specification — Null-aware elements.
Cascades like `obj.._parent!.onChildrenChanged(this)` fail to parse because the cascade subsection rule only allowed an assignable selector followed by argument parts, with no way to apply the postfix `!` null assertion between selectors. The non-cascade `selector` rule already handles this via the general selector form. Mirror that here: after the leading cascade selector and after each subsection's assignable selector, accept argument parts and `!` interleaved. This matches the shape Dart programs actually use when chaining null-asserted member access in cascades (common in flame, flutter widget code, etc.).
`dart:ffi` Struct definitions sometimes declare external fields whose
name is a built-in identifier (`get`, `set`, `operator`). For example
in the `colibri_stateless` package:
@ffi.Packed(1)
final class colibri_StatusCondition extends ffi.Struct {
external colibri_StatusCondition_DataType get;
}
These names are reserved keywords only in restricted contexts; in field
declarations they are valid identifiers. Accept them as aliases of
`identifier` in the static external field arm so packages using ffi
struct conventions parse cleanly.
Dart 2.19+ allows the library directive to be written without a name (`library;`) so the file can carry a doc comment or annotation that documents the library as a whole, without forcing the author to invent a globally-unique library identifier. The reference is the language versioning in the Dart language spec section 'Libraries' (the library declaration's name part is optional since the library declaration syntax was clarified for the unnamed-library form). Without this, the parser inserts a synthetic MISSING identifier and emits a partial parse for any file that uses the form, e.g. `just_storage`'s `example/di_setup_example.dart` and many other documentation-only library files.
Per the Dart language spec section 'Symbols', a symbol literal accepts: - a single identifier or dotted identifier list (e.g. `#a.b.c`) - a single operator token (e.g. `#+`, `#==`, `#[]`, `#[]=`, `#~`) The previous grammar only accepted `#identifier`, so common forms used in serialization/codegen libraries (e.g. `firebase_dart`'s `#UploadTask.pause` and `build_runner` symbol-as-key patterns) failed to parse.
A record literal with exactly one named field requires a trailing comma (`(name: value,)`) to be unambiguously a record rather than a labeled parenthesized expression. The grammar previously accepted `(label: expr)` (no comma) and `(label: expr, more,)` (2+ fields) but rejected the unambiguous single-named-field form. This pattern shows up in real code returning records with one named component, e.g. `return (prefix: syntheticImport.prefix,);`. See Dart 3 record literal grammar in the language spec.
When this branch added `alias($._operator, $.identifier)` to `initialized_identifier` and `_declared_identifier` (so that `String operator;` parses), the parser started accepting the `'operator'` keyword token in identifier position. Tree-sitter still emits the underlying anonymous keyword node, so the global `"operator" @keyword` query in highlights.scm fired in places where `operator` is being used as a variable name (e.g. `var operator = 1;`), breaking the `keywords.dart` highlight test. Fix: drop `"operator"` from the global keyword list and add a scoped query that highlights it as a keyword only inside `operator_signature`. Mirrors the existing pattern used for `get`/`set` (which already work this way and pass the same test).
AlexLaroche
added a commit
to AlexLaroche/ocaml-tree-sitter-semgrep
that referenced
this pull request
May 5, 2026
Brings in the merged upstream PR UserNobody14/tree-sitter-dart#97 with 16 grammar improvements + 1 highlight fix: - Empty record literal `()` - Dart 3.10 dot-shorthand in case patterns and `const` invocations - `get`/`set`/`operator`/`Function` as ordinary identifiers in initialized_identifier, _declared_identifier, assignable_expression, label, and static external field name positions - Abstract instance fields (`abstract <type>? <ids>;`) - Top-level external variables and external instance fields with `final`/`covariant` (dart:js_interop extension types) - Empty/null statement (`;`) - Trailing comma in for-loop update expressions - Full expression on field initializer right-hand side - Null-aware key in map literal entry (Dart 3 null-aware elements) - Null-assertion `!` and argument parts within cascade subsections - Unnamed library directive (`library;`) - Symbol literals with dotted ids and operators (`#a.b.c`, `#+`, `#==`) - Single-named-field record literal with trailing comma (`(name: v,)`) - Scoped `operator` keyword highlight to operator_signature Validation against the pub.dev catalog: 5,285 packages scanned, 5,147 clean parses (97.4%), 56 PartialParsing (down from ~102 before this branch), 0 control regressions across iterations.
yosefAlsuhaibani
pushed a commit
to semgrep/semgrep
that referenced
this pull request
May 15, 2026
…ilities (semgrep/semgrep-proprietary#6332) > **Draft — gated on semgrep/ocaml-tree-sitter-semgrep#590 landing and an r2c admin running `./release dart` to publish a real release commit to `semgrep/semgrep-dart`. See "Before marking ready" below.** ## Summary - Bumps the `languages/dart/tree-sitter/semgrep-dart` submodule to a regenerated artifact built from upstream `UserNobody14/tree-sitter-dart@02e9bb19` (13 commits ahead of the May-2023 pin), and adapts `Parse_dart_tree_sitter.ml` to the new CST shape. - Resolves the headline Dart 3 parsing gap: empty-paren `object_pattern` (e.g. `_MyClass()` arms in `switch`), now common in Flutter (upstream PR [#93](UserNobody14/tree-sitter-dart#93)). - Resolves the `@annotation` + record-return-type method gap (e.g. `@override (R, T) method() {…}`, the Elm-style architecture pattern in Flutter) — this was the dominant remaining `PartialParsing` cause on the pub.dev catalog (upstream PR [#98](UserNobody14/tree-sitter-dart#98)). - Also picks up: 16 grammar fixes from the upstream parser-improvements PR ([#97](UserNobody14/tree-sitter-dart#97)) — empty record literal `()`, dot-shorthand patterns, abstract/external fields, null-aware map keys, dotted/operator symbol literals, single-named-field record literals, unnamed library directive, and more. - Drops the stale "tree-sitter-dart is currently buggy" segfault gate in `Parse_target.ml` and the `Lang.Dart` exclusion in `Rule_fetching.ml` — the original repro (`': string (* filename *)'`) now exits cleanly. - Fixes three parser-side bugs in `Parse_dart_tree_sitter.ml` discovered during pattern-validation work that reproduce on `develop` too: 1. **Uninitialized local variable declarations were silently dropped.** `String z;` / `late String w;` inside a function body never made it into the AST, so patterns like `String $X;` could not match local declarations. Fixed by emitting `(id, None)` instead of `[]` when the optional `= expr` is absent. 2. **Patterns starting with a Dart statement keyword silently misparsed.** `if (kDebugMode) { ... }` parsed as a function definition (with `if` as the return type), `await foo();` parsed as a bare `Call(foo, [])` with `await` stripped. Detect statement-keyword starts up-front and route through the `__SEMGREP_EXPRESSION` path, bypassing the misleading file-parse. 3. **Function-definition patterns silently degraded to calls.** `void test() { ... }` reinterpreted as `Call(test, [])` (losing return type, body, and function context), so `pattern-inside: void test() { ... }` could never match a function definition. Tightened the forward-decl ambiguity heuristic so only truly-ambiguous shapes (no return type, no params, empty body, no attrs) collapse to a Call. - Adds three Dart-side metavariable capabilities that other major languages already had: 1. **Typed metavariable `$X as T`** — in pattern mode, the existing Dart `as` cast is reinterpreted as `TypedMetavar` when the LHS is a metavariable (TS/Java/Swift use the same trick). 2. **`metavariable-type: T` filter** — Dart registered in `Parse_metavariable_type` (`x as %s` wrap, `Cast` unwrap). 3. **Metavariables inside string interpolations** — `print("hello $X")` now binds `$X` to the interpolation expression on Dart targets, matching Semgrep's convention everywhere else. Real Dart `$name` interpolations (lowercase) keep the literal-text behavior so existing patterns still match. ## Validation Generated the artifact via the standard flow (`make && make install && cd lang && ./test-lang dart && ./release dart --dry-run`) on `ocaml-tree-sitter-semgrep` with the bumped submodule, then tested the regenerated `parse-dart`: | Corpus | Old grammar (`80e23c07`) | New grammar (`02e9bb19`) | |---|---|---| | 199 random `.dart` files from [`flutter/flutter@master`](https://github.com/flutter/flutter) (1-30 KB) | 196 ok / 3 fail | **199 ok / 0 fail** | | 5,285 latest-version packages on pub.dev | ~102 `PartialParsing` | **56 `PartialParsing` (1.1%)** | The 3 Flutter files that previously failed all live inside developer tooling and exercise Dart 3 patterns: - [`dev/bots/check_tests_cross_imports.dart`](https://github.com/flutter/flutter/blob/master/dev/bots/check_tests_cross_imports.dart) — `switch (this) { _MaterialLibrary() => ..., _CupertinoLibrary() => ..., _OtherLibrary() => ... }` - [`dev/bots/custom_rules/no_stop_watches.dart`](https://github.com/flutter/flutter/blob/master/dev/bots/custom_rules/no_stop_watches.dart) — `Element() || null`, nested `InterfaceType(element: ...)` - [`dev/bots/custom_rules/render_box_intrinsics.dart`](https://github.com/flutter/flutter/blob/master/dev/bots/custom_rules/render_box_intrinsics.dart) — `switch (node.parent) { ... }` ## New tests (8) Added regression tests covering every distinct fix and capability in this branch: | Test | Covers | |---|---| | `tests/parsing/dart/switch_object_pattern.dart` | Empty-paren / field-bearing / generic / guarded `object_pattern` arms in switch (Dart 3 `#93`) | | `tests/parsing/dart/dart3_grammar_features.dart` | Empty record `()`, single-named record `(name: v,)`, dot-shorthand `.foo`/`.new`, null-aware map element `?key: value`, null-assertion `!` in cascade, unnamed library directive, dotted/operator symbol literals (`#a.b.c`, `#==`), `get`/`set` as identifiers | | `tests/patterns/dart/uninit_local_var.{dart,sgrep}` | `String $X;` matches uninitialized locals (parser fix #1) | | `tests/patterns/dart/function_def_void.{dart,sgrep}` | `void test() { ... }` matches function definitions (parser fix #3) | | `tests/rules/if_pattern_dart.{yaml,dart}` | `if (kDebugMode) { ... }` matches real If statements (parser fix #2 — `if` keyword routing) | | `tests/rules/await_pattern_dart.{yaml,dart}` | `await fetch($URL)` matches only awaited calls (parser fix #2 — `await` keyword routing) | | `tests/rules/typed_metavar_dart.{yaml,dart}` | `print($X as String)` binds against String args/decls only (typed metavariable) | | `tests/rules/metavar_type_dart.{yaml,dart}` | `metavariable-type: String` filter (typed metavariable) | | `tests/rules/string_interp_metavar_dart.{yaml,dart}` | `print("hello $X")` binds the interpolation expression | ## Commit groups This branch is 21 commits — grouped by intent: 1. **Submodule bump (commit 1)** — `chore(dart): bump tree-sitter-dart and adapt OCaml mapper for Dart 3` style commit at the base. 2. **CST-shape adaptations (commits 2–10)** — one commit per upstream grammar change, each adjusting `Parse_dart_tree_sitter.ml` to consume the new CST shape: abstract/external var, label/operator identifiers, empty-statement, external instance fields, for-loop/field-initializer changes, null-aware map key, null-assertion in cascades, ffi external get/set fields, full symbol literals, single-named record, record-return method. 3. **Parser bug fixes (commits 11–14)** — independent of the bump, surfaced during pattern validation: uninitialized-local-decl drop, statement-keyword pattern routing, `await`/`rethrow` keyword routing, function-definition pattern degradation. 4. **Pattern-mode capabilities (commits 17–19)** — typed metavariable `$X as T` + `metavariable-type` registration, metavariables inside string interpolations, function-def pattern matching. 5. **Cleanup (commit 15)** — drop the stale segfault gate in `Parse_target.ml` and the `Lang.Dart` exclusion in `Rule_fetching.ml`. 6. **Validation pointer (commit 16)** — _temporary_ redirect of `.gitmodules` so this branch resolves the validation artifact from `AlexLaroche/semgrep-dart`. **Drop before merge.** 7. **Style (commit 20)** — `style(dart): apply ocamlformat 0.29.0` to satisfy CI's `lint-ocaml` hook (CI uses 0.29.0; recent edits were made with a local 0.27.0 whose `break-cases` differs). 8. **Tests (commits 21–22)** — 8 new tests covering every fix and capability above (table in the previous section). ## Before marking ready for review - [x] semgrep/ocaml-tree-sitter-semgrep#590 lands (bumps the upstream tree-sitter-dart submodule there to `02e9bb19`). - [x] An r2c admin runs `./release dart` on `ocaml-tree-sitter-semgrep` to publish a real release commit on `semgrep/semgrep-dart`. - [x] Drop the `chore(dart): point semgrep-dart submodule URL at fork for validation` commit. - [x] Re-point the submodule pointer from `c5a5e6c1` (on `AlexLaroche/semgrep-dart`) to whatever real release SHA appears on `semgrep/semgrep-dart`. - [ ] CI green on this repo. ## Test plan - [x] `make core` clean against the bumped submodule. - [x] Dart lang-parsing tests pass; Dart sgrep-pattern tests pass. - [x] Standalone `parse-dart`: 199/199 files in `flutter/flutter@master` parse cleanly (was 196/199). - [x] pub.dev: 5,229/5,285 fully clean (was ~5,183), 56 `PartialParsing` (was ~102). - [x] Original "segfault" repro (`: string (* filename *)`) — exits cleanly, no signal, on both `parse-dart` and `semgrep-core -dump_pattern`. - [x] Pre-commit `lint-ocaml` hook passes locally with ocamlformat 0.29.0. - [ ] CI on this repo (semgrep/semgrep). Synced from OSS PR #11678 Author: @AlexLaroche Imported by: @yosefAlsuhaibani Closes #11678 OSS-sync-author: Alexandre Laroche <larochealexandre@hotmail.com> Test plan: synced from Pro df36084f8b8ee40724eab0322e710c3369efe6f5
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.
A set of small, targeted grammar fixes uncovered while running real-world Dart packages through tree-sitter-dart. Each commit is self-contained, cites the relevant Dart spec section, and ships with a corpus test (where the fix introduces new AST shapes). All commits keep the existing 175-test corpus and the syntax-highlighting test suite passing.
Validation
I scanned the pub.dev catalog (latest version of each package) using the parser produced by this branch, then re-scanned every previously-failing package against the freshly-built parser to discount stale results. Final counts:
PartialParsingerrorsFor reference, an earlier snapshot of the same corpus had 102 packages with
PartialParsing. The seventeen commits in this PR clear roughly half of those (~45% drop on that snapshot), with 0 regressions on the previously-clean control sample at every iteration. The remaining 56 are dominated by patterns that need deeper grammar restructuring than incremental edits can address — primarily@overridefollowed by a record return type with named fields (e.g.@override (String name, int age) foo();), where tree-sitter commits to the annotation's argument-list path before discovering the failure.Commits
()—_record_literal_no_constnow accepts the unit/empty record. The empty form gets a negative dynamic precedence sof<T>()still parses as type-args + empty-args; new GLR conflicts vsrecord_typeand_strict_formal_parameter_list.constinvocations —case .red:,case .new():,case .fromRGB(1, 2, 3):now parse.const_object_expressiongainsconst_builtin + dot_shorthand + argumentsforconst Color c = const .new();.get/setas identifier ininitialized_identifier—int set = 0;andString get = 'foo';parse as fields. Mirrors the existing aliasing in_declared_identifier.get/set/Functionas identifier in assignable expressions —set = 5;andFunction.apply(...)parse correctly. Aliases_get,_set, and_function_builtin_identifiertoidentifierin_primaryandassignable_expression.abstract <type>? <ids>;/abstract final .../abstract covariant ...todeclaration, andexternal <type>? <ids>;(with optionalfinal/late) to_top_level_definition.get/set/Functionas label andoperatoras identifier —f(get: x, set: y)andString operator;parse. Adds the aliases to thelabelrule and toinitialized_identifier/_declared_identifier.;—if (cond) ;and stray;now parse via a newempty_statementrule.final/covariant— Addsexternal final <type>? <ids>andexternal covariant <var-or-type> <ids>todeclarationfordart:js_interopextension types.for (var i = 0; i < n; i += 15,) { ... }parses (commaSep→commaSepTrailingComma).A(int? x) : x = x ??= 5;now parses (_real_expression→_expression).{?maybeKey: value, key: ?maybeValue}parses (Dart 3 null-aware elements).!and argument parts within cascade subsections —obj.._parent!.onChildrenChanged(this)parses (common in flame, flutter widget code). After the leading cascade selector and after each subsection's assignable selector, accept argument parts and!interleaved — mirrors the generalselectorform used in non-cascade chains.external SomeType get;(used bydart:ffiStruct conventions, e.g.colibri_stateless) parses. Aliasesget/set/operatortoidentifierin the_external_and_static + _type + identifierarm.library;) — Dart 2.19+ permits an unnamedlibrary;to attach a doc comment / annotation to a library file without a name. Makes thedotted_identifier_listinlibrary_nameoptional.#foo.bar.baz) or a single operator token (#+,#==,#[],#[]=,#~,#|,#&,#^, plus the relational, shift, additive, multiplicative ops). The previous grammar only accepted#identifier.(name: value,)parses as a record. Dart 3 requires the trailing comma here to disambiguate from a labeled parenthesized expression.operatorkeyword highlight tooperator_signature— Onceoperatoris aliased as an identifier (commit 6), the global"operator" @keywordquery inhighlights.scmwould tag occurrences likevar operator = 1;as a keyword. Drop it from the global list and add a scoped query underoperator_signature, mirroring the pattern already used forgetandset.Spec references
Each commit message cites the relevant section of the Dart Language Specification (Records, Patterns, Built-in Identifiers, Statements, Constructor field initializers, Null-aware elements, External members, Abstract instance variables, Symbols, Libraries).
Generated files
src/grammar.json,src/node-types.json, andsrc/parser.care regenerated fromgrammar.jsviatree-sitter generateand committed as part of each grammar.js change, matching the convention already used in this repo.