Repository navigation
[code_builder] Add null-aware cascades, cascade index, and fluent cascade helpers - #2540
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds several new expression builders to code_builder to support null-aware cascades, cascaded index assignments, and fluent multi-property cascade chains, along with comprehensive unit tests. The review feedback suggests improving API completeness and symmetry by adding nullSafeCascadeInvoke and nullSafeCascadeAssigns helpers, and recommends correcting the DartDoc comments to avoid starting with 'Returns' on the first line.
| /// Returns an expression invoking `..<name>(...)` on this expression. | ||
| Expression cascadeInvoke( | ||
| String name, [ | ||
| Iterable<Expression> positionalArguments = const [], | ||
| Map<String, Expression> namedArguments = const {}, | ||
| List<Reference> typeArguments = const [], | ||
| ]) => cascade(name).call(positionalArguments, namedArguments, typeArguments); |
There was a problem hiding this comment.
For completeness and API symmetry with cascadeInvoke and nullSafeCascadeAssign, consider adding a nullSafeCascadeInvoke helper to support invoking methods via a null-aware cascade (?..). Note that DartDoc comments should not start with 'Returns' on the first line; instead, describe what the function does.
| /// Returns an expression invoking `..<name>(...)` on this expression. | |
| Expression cascadeInvoke( | |
| String name, [ | |
| Iterable<Expression> positionalArguments = const [], | |
| Map<String, Expression> namedArguments = const {}, | |
| List<Reference> typeArguments = const [], | |
| ]) => cascade(name).call(positionalArguments, namedArguments, typeArguments); | |
| /// Invokes ..name(...) on this expression. | |
| Expression cascadeInvoke( | |
| String name, [ | |
| Iterable<Expression> positionalArguments = const [], | |
| Map<String, Expression> namedArguments = const {}, | |
| List<Reference> typeArguments = const [], | |
| ]) => cascade(name).call(positionalArguments, namedArguments, typeArguments); | |
| /// Invokes ?..name(...) on this expression. | |
| Expression nullSafeCascadeInvoke( | |
| String name, [ | |
| Iterable<Expression> positionalArguments = const [], | |
| Map<String, Expression> namedArguments = const {}, | |
| List<Reference> typeArguments = const [], | |
| ]) => nullSafeCascade(name).call(positionalArguments, namedArguments, typeArguments); |
References
- At least all public members should have documentation, answering the why. (link)
- DartDoc comments should not start with "Returns" if it's not the first line of the DartDoc. The first line should describe what the function does, and subsequent lines can describe the return value.
| /// Chains a `..<name> = <value>` cascade assignment for each entry in | ||
| /// [assignments] onto this expression. | ||
| Expression cascadeAssigns(Map<String, Expression> assignments) => | ||
| assignments.entries.fold<Expression>( | ||
| this, | ||
| (target, entry) => target.cascadeAssign(entry.key, entry.value), | ||
| ); |
There was a problem hiding this comment.
To support chaining multiple assignments starting with a null-aware cascade, consider adding a nullSafeCascadeAssigns helper. In Dart, only the first cascade operator in a chain should be null-aware (?..), while subsequent operators must be regular cascades (..). A dedicated helper can handle this transition automatically.
| /// Chains a `..<name> = <value>` cascade assignment for each entry in | |
| /// [assignments] onto this expression. | |
| Expression cascadeAssigns(Map<String, Expression> assignments) => | |
| assignments.entries.fold<Expression>( | |
| this, | |
| (target, entry) => target.cascadeAssign(entry.key, entry.value), | |
| ); | |
| /// Chains a ..name = value cascade assignment for each entry in | |
| /// [assignments] onto this expression. | |
| Expression cascadeAssigns(Map<String, Expression> assignments) => | |
| assignments.entries.fold<Expression>( | |
| this, | |
| (target, entry) => target.cascadeAssign(entry.key, entry.value), | |
| ); | |
| /// Chains a ?..name = value cascade assignment for the first entry, | |
| /// followed by ..name = value for subsequent entries in [assignments] | |
| /// onto this expression. | |
| Expression nullSafeCascadeAssigns(Map<String, Expression> assignments) { | |
| if (assignments.isEmpty) return this; | |
| final first = assignments.entries.first; | |
| final rest = assignments.entries.skip(1); | |
| return rest.fold<Expression>( | |
| nullSafeCascadeAssign(first.key, first.value), | |
| (target, entry) => target.cascadeAssign(entry.key, entry.value), | |
| ); | |
| } |
References
- At least all public members should have documentation, answering the why. (link)
|
@hkarmoush thank you! I think the gemini suggestions are...reasonable, but make sure you double check and add tests. |
…or API symmetry Addresses review feedback on dart-lang#2540: nullSafeCascadeInvoke mirrors cascadeInvoke for null-aware method cascades, and nullSafeCascadeAssigns chains a null-aware first assignment followed by regular cascade assignments, since only the first cascade in a chain may be null-aware.
natebosch
left a comment
There was a problem hiding this comment.
I don't think we should land the helpers like cascadeInvoke.
I do like some aspects of nullSafeCascadeAssigns - it handles the first-uses-question-mark behavior, and is a closer match to semantics than the existing syntax matching API. I don't like how it specializes a single form of cascades.
If we were to restart or introduce breaking changes I suspect there's a more orthogonal breakdown we can have for the API without needing to specialize this much, but for where we are I think the best thing would be to land nullSafeCascade, cascadeIndex, and nullSfeCascadeIndex.
Adds Expression.nullSafeCascade, cascadeIndex, and nullSafeCascadeIndex to support the `?..`, `..[]`, and `?..[]` operators. Scoped to these three core methods per review feedback on the upstream PR (dart-lang#2540), which flagged the additional cascadeInvoke/cascadeAssign/cascadeAssigns style helpers as unnecessary API surface for now.
Adds Expression.nullSafeCascade, cascadeIndex, and nullSafeCascadeIndex to support the ?.., ..[], and ?..[] operators. Scoped to these three core methods per review feedback on this PR, which flagged the additional cascadeInvoke/cascadeAssign/cascadeAssigns style helpers as unnecessary API surface for now.
9e6df98 to
d187f09
Compare
|
Pushed a scoped-down version landing just |
|
@natebosch Following up here — the branch has been scoped down to just |
Revisions updated by `dart tools/rev_sdk_deps.dart`. native (https://github.com/dart-lang/native/compare/f628b26..27f7678): 27f767827 Mon Sep 28 14:51:28 2026 +0200 Daco Harkes [hooks_runner] Remove `KernelAssets` and `Target` (dart-lang/native#3703) ddc7d6deb Mon Sep 28 05:40:03 2026 +0200 Jakob Kordež [ffigen] Skip typedefs to function pointers with unsupported parameter types (dart-lang/native#3694) 177d283d9 Mon Sep 28 04:30:28 2026 +0200 Jakob Kordež [ffigen] Generate bindings for C++ enums, structs and unions nested in namespaces and records (dart-lang/native#3644) 5ee8f67d8 Mon Sep 28 11:39:14 2026 +1000 Liam Appelbe [objective_c] Clarify block isolate scoping (dart-lang/native#3692) 25f090116 Fri Sep 25 12:09:12 2026 +1000 Liam Appelbe [ffigen][jnigen] Deprecate YAML config (dart-lang/native#3654) 8364413eb Fri Sep 25 10:09:50 2026 +1000 Liam Appelbe [jnigen] Document nullability (dart-lang/native#3690) e8d518574 Thu Sep 24 18:02:46 2026 +1000 Liam Appelbe [ffigen] Code snippet-ify ffigen and objective_c (dart-lang/native#3685) 663385955 Thu Sep 24 03:21:53 2026 +0300 Manar Elhabbal [ffigen] prevent setting both cpp and objectiveC in ffigenerator (dart-lang/native#3687) 1cf40952a Thu Sep 24 09:27:09 2026 +1000 Liam Appelbe [objective_c] Remove arm64e build hack (dart-lang/native#3680) c44550b6e Wed Sep 23 13:01:33 2026 +0200 Simon Binder [web_assets] Scaffold package (dart-lang/native#3610) 57b4bad69 Wed Sep 23 16:21:27 2026 +1000 Liam Appelbe [swift2objc] Code snippet-ify swift2objc (dart-lang/native#3684) 5cee1d0be Wed Sep 23 10:16:29 2026 +0530 Rachit Vikram Singh [ffigen] Add Input.appendCompilerOptions to append clang args to defaults (`#3630`) (dart-lang/native#3676) 225a648df Wed Sep 23 14:37:13 2026 +1000 Liam Appelbe [jnigen] Code snippet-ify jnigen and co (dart-lang/native#3641) 2030decf9 Wed Sep 23 13:51:24 2026 +1000 Liam Appelbe [infra] Move snippets_tool to its own package (dart-lang/native#3683) 6fa2beea2 Wed Sep 23 12:14:28 2026 +1000 Liam Appelbe [objective_c] Revert `#3664` (dart-lang/native#3681) ed7605f8a Tue Sep 22 12:03:22 2026 +0530 Sivapriya [jni] Add deep conversion utils (dart-lang/native#3635) 8574b7422 Tue Sep 22 08:31:11 2026 +0200 Lukas Klingsbo [ffigen] Allow package:cli_util 0.5.x and 0.6.x (dart-lang/native#3673) test (https://github.com/dart-lang/test/compare/9946c95..4f92d53): 4f92d530 Fri Sep 25 16:46:22 2026 -0700 Nate Bosch Unskip a test on windows (dart-lang/test#2772) 7fb84866 Fri Sep 25 14:52:39 2026 -0700 Nate Bosch Bundle skips at the end of the GitHub reporter (dart-lang/test#2768) 603e5ec5 Thu Sep 24 14:00:41 2026 -0700 Nate Bosch Clean up the runner's temporary directories (dart-lang/test#2763) 4bf04d90 Thu Sep 24 12:41:15 2026 -0700 Nate Bosch Refactor signal handling in executable.dart (dart-lang/test#2765) 2c660a84 Tue Sep 22 15:33:34 2026 -0700 Nate Bosch Regenerate mono_repo output (dart-lang/test#2767) tools (https://github.com/dart-lang/tools/compare/3614ce0..d87eaf7): d87eaf79 Thu Sep 24 08:31:33 2026 -0700 Kevin Moore fix(benchmark_harness): migrate subprocess spawning to package:cli_util (dart-lang/tools#2585) 8ccf8ccf Wed Sep 23 05:09:10 2026 +0530 Aman Maurya [string_scanner] Extract a mixin for LineScanner and deduplicate eager scanning logic (dart-lang/tools#2569) afcdd6d9 Tue Sep 22 15:38:07 2026 +0200 Morgan :) Document that watch events converge on filesystem state. (dart-lang/tools#2607) f2157cbc Tue Sep 22 04:52:58 2026 +0300 Hasan Armoush [code_builder] Add null-aware cascades, cascade index, and fluent cascade helpers (dart-lang/tools#2540) web (https://github.com/dart-lang/web/compare/e5c6c02..ec71a4c): ec71a4c Mon Sep 28 10:07:01 2026 -0700 Kevin Moore [js_interop_gen] Require `code_builder: ^4.12.0` and update golden headers (dart-lang/web#576) [email protected] Change-Id: I766bc5b2b1297e3251990821bb73387ba685e39b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/556320 Commit-Queue: Nate Bosch <[email protected]> Reviewed-by: Brian Quinlan <[email protected]>
Closes #2524.
Expressioncould already docascade,nullSafeProperty, andindex, but a few things you'd expect were missing:?..(null-aware cascade)expr.index(k).assign(v)emitsexpr[k] = v— no way to getexpr..[k] = vThis adds:
nullSafeCascade,cascadeIndex,nullSafeCascadeIndex— same shape as the existingcascade/nullSafeProperty/index, just with the right join operatorcascadeAssign,nullSafeCascadeAssign,cascadeInvoke,cascadeAssigns— small conveniences built on top, matching the API proposed in the issueNo new AST node types or visitor changes — everything reuses the existing
BinaryExpressionmachinery, so it's a pretty low-risk addition.Added tests for each new method, ran
dart formatanddart analyze(clean), and the fullcode_buildertest suite passes (408/408).