fix: preserve object and array spread syntax in template bindings#260
Merged
Brooooooklyn merged 3 commits intovoidzero-dev:mainfrom May 5, 2026
Conversation
Object and array spread expressions in Angular template bindings were
silently dropped during compilation, e.g. `[prop]="{ ...base, key: val }"``
would emit `{ key: val }` with the spread lost entirely.
The fix spans the full compilation pipeline:
- ingest: handle LiteralMapKey::Spread when converting AST→IR
- ir/expression: add spreads: Vec<bool> parallel field to all literal
map/array IR types to track which entries are spreads
- resolve_names: propagate spreads through name resolution
- pure_literal_structures: emit spread entries in pure function wrappers
- reify/ir_expression: emit spread entries when lowering IR→output AST
- reify/angular_expression: handle SpreadElement in LiteralArray arm
- emit: fix convert_ast_for_pure_function_body to handle spreads in
both LiteralMap and LiteralArray
- output/ast: clone_in and chaining preserve is_spread on LiteralMapEntry
- output/oxc_converter: handle SpreadProperty in object expressions
- defer_configs, pipe_variadic: maintain spreads parallel-vec invariant
Adds 9 snapshot tests covering object spread, array spread, multiple
spreads, spread-with-pipe, spread-at-end, chained bindings, and arrow
function body spread. Adds TS NAPI tests for both object and array spread.
Member
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 936c302549
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Pooled pure-function bodies for `[a]` and `[...a]` (and object equivalents) collided in the constant pool because the key generation for DerivedLiteralArray/DerivedLiteralMap ignored the `spreads` parallel array, causing one binding to silently inherit the other's runtime semantics. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brooooooklyn
approved these changes
May 5, 2026
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
Object and array spread expressions in Angular template bindings were silently dropped during compilation with no error or warning:
The root cause was that
LiteralMapKey::Spreadwas skipped iningest.rsduring AST→IR conversion, and no subsequent pipeline stage had any representation for spread entries. Array spread ([...a, 'val']) had the same gap inangular_expression.rs.Changes
The fix spans the full compilation pipeline:
ir/expression.rs: Addedspreads: Vec<bool>parallel field toIrLiteralMapExpr,DerivedLiteralMapExpr,IrLiteralArrayExpr,DerivedLiteralArrayExprto track which entries are spreadspipeline/ingest.rs: HandleLiteralMapKey::Spreadwhen converting AST→IR (previously skipped with a comment)pipeline/phases/resolve_names.rs: Propagate spreads through name resolutionpipeline/phases/pure_literal_structures.rs: Emit spread entries in pure function wrapperspipeline/phases/reify/ir_expression.rs: Emit spread entries when lowering IR→output ASTpipeline/phases/reify/angular_expression.rs: HandleSpreadElementinLiteralArrayarmpipeline/emit.rs: Fixconvert_ast_for_pure_function_bodyto handle spreads in bothLiteralMapandLiteralArrayoutput/ast.rs:clone_incorrectly copiesis_spreadonLiteralMapEntryoutput/emitter.rs: Emit...exprfor spread entries in both object and array literalsoutput/oxc_converter.rs: HandleSpreadPropertyin object expressions (previously skipped)pipeline/phases/chaining.rs: Preserveis_spreadwhen cloning entries during chainingpipeline/phases/defer_configs.rs,pipe_variadic.rs: Maintainspreadsparallel-vec invariantTests
oxc_converter.rsfor theSpreadPropertypathOutput verified against official Angular 21.2.11 compiler — functionally identical (pure function wrapping, parameter naming, binding instructions).