Skip to content

fix: Match arm block IIFEs missing return on last expression#974

Merged
milkyskies merged 9 commits intomainfrom
fix/match-arm-block-iife-return
Apr 3, 2026
Merged

fix: Match arm block IIFEs missing return on last expression#974
milkyskies merged 9 commits intomainfrom
fix/match-arm-block-iife-return

Conversation

@milkyskies
Copy link
Copy Markdown
Collaborator

Summary

  • Match arm bodies with multiple statements (blocks) get wrapped in IIFEs during codegen
  • The last expression in the block was emitted without return, so the IIFE returned undefined
  • This caused buildColumns to return undefined instead of the computed array, crashing with TypeError: undefined is not an object (evaluating 'boardColumns.find')

Fix

Added return before the last expression item in match arm block IIFEs, in both the general case and the string-pattern case.

Test plan

  • cargo test — 1252 tests pass (1 new regression test)
  • floe check + floe build on example apps — no errors
  • Verified the fix on the affected .fl file

🤖 Generated with Claude Code

When a match arm body is a block (multiple statements), the codegen
wraps it in an IIFE. The last expression in the block was emitted
without `return`, so the IIFE returned `undefined` instead of the
computed value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added the fix label Apr 3, 2026
milkyskies and others added 8 commits April 3, 2026 16:35
Sub-codegens (used for emit_expr_string, emit_arg_strings, and pipe
emission) set has_jsx when encountering JSX, but never propagated it
back to the parent codegen. This caused files containing JSX inside
match arms, callbacks, or pipes to be output as .ts instead of .tsx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a stdlib template produces a ternary (e.g. Option.unwrapOr:
`$0 !== undefined ? $0 : $1`) and the result is used as $0 in
another template with member access (e.g. `$0.filter(...)`), the
member access bound to the false-branch instead of the whole ternary.

Now wraps ternary-containing args in parens when the template
accesses them with `.`, `[`, or `(`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Arrows whose body is a Block (e.g. from `use` callback desugaring)
used `emit_block_items` which doesn't add `return` to the last
expression. Now uses `emit_block_expr_with_return` instead, matching
how function bodies handle implicit returns.

This caused `use key <- Option.guard(...)` callbacks to return
undefined, making the component render nothing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ports

collect_value_used_names didn't recurse into ExprKind::Object or
ExprKind::Grouped/Spread, causing names used inside JSX style props
(e.g. `style={{ color: Record.get(PRIORITY_COLORS, p) }}`) to be
treated as type-only imports and erased at runtime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All Option stdlib functions used strict `!== undefined` checks, which
missed `null` values from serde/JSON serialization. Changed to loose
`!= null` which catches both null and undefined, matching how
JavaScript APIs and Rust's serde actually represent absent values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds syntax for default imports: `import [trusted] X from "module"`.
Compiles to `import X from "module"` in TypeScript.

Previously Floe only supported named imports (`import { X } from "module"`),
which breaks at runtime for packages that only have a default export
(e.g. react-markdown) in non-bundled environments.

closes #975

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`match x > 0 { true -> ... }` emitted `x > 0 === true` where `===`
binds tighter than `>`, evaluating as `x > (0 === true)`. Now wraps
binary/pipe/unary subjects in parens: `(x > 0) === true`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Option<T> in type annotations and .d.fl.ts now emits T | null | undefined
instead of T | undefined. This matches the runtime behavior (stdlib
already uses != null) and allows TS callers to pass null (standard in
React, JSON, serde) without type errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@milkyskies milkyskies merged commit 9c725fa into main Apr 3, 2026
1 check passed
@milkyskies milkyskies deleted the fix/match-arm-block-iife-return branch April 3, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant