Skip to content

Preserve attribute arguments and actor-handler attrs in the AST#80

Merged
StreamDemon merged 1 commit into
feature/parser-enhancements-basefrom
feature/parser-enh-attr-args
Jul 2, 2026
Merged

Preserve attribute arguments and actor-handler attrs in the AST#80
StreamDemon merged 1 commit into
feature/parser-enhancements-basefrom
feature/parser-enh-attr-args

Conversation

@StreamDemon

@StreamDemon StreamDemon commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Attribute now preserves its arguments: args: Vec<AttrArg> plus a span covering @ through the closing paren. Previously @mailbox(capacity: 2048) / @supervisor(strategy: "one_for_one") dropped everything inside the parens at parse time.
  • AttrArg mirrors the §16 grammar (attr_arg = IDENT [ ":" expr | "=" expr | "(" expr ")" ] | expr). Only IDENT ":" needs lookahead; the = and call alternatives are valid expressions, so they parse as expressions and canonicalize to the most specific attr form afterwards (self/Self excluded — they are keywords, not IDENT).
  • Actor handlers become Handler { attrs, function } — a handler is a fn_def (§16), so its attrs (@mailbox(...)) are no longer dropped outright. Item-position attrs stay hoisted on Item.attrs during bootstrap.
  • The now-unused skip_balanced_after_open helper is removed.
  • Five new tests assert argument shapes and spans structurally (derive lists, named args, assigned/call/expr args, bare attrs, handler attrs).

Fifth sub-PR of the enhancement wave (PR #71 roadmap: "Preserve attribute arguments in the AST"). Known deferral: attrs written before an actor field are still discarded — §16 gives fields no attrs, and rejecting them is a behavior change outside this wave's scope (same tolerance item-position attrs already have on mod/use/const).

Related Issue

None (PR #71 roadmap item). Issue #78 (spurious type-arg error) was found while designing attr_arg and is tracked separately — it is a behavior fix, so it deliberately does not ride this wave.

Spec Sections Affected

None — implementation quality only; parsing follows the existing §16 attrs/attr_args/attr_arg productions.

Checklist

  • Code follows the Sploosh design principles (one way to do it, explicit over implicit, etc.)
  • Documentation updated in relevant docs/ pages — N/A, no behavior change
  • Tests added or updated
  • All build targets still compile (if applicable)
  • Spec-only PR (skip Build Targets section if checked)

Build Targets Tested

  • cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace all green locally (54 tests).

Test Plan

  • attribute_arguments_are_preserved@derive(Debug, Eq) idents + full attribute span.
  • attribute_named_args_are_preserved@supervisor(strategy: "one_for_one", max_restarts: 5) named args with literal payloads.
  • attribute_assigned_call_and_expr_args_are_preserved=, call, and bare-expr alternatives.
  • bare_attribute_has_no_args_and_name_span@test span anchors.
  • actor_handler_attributes_are_preserved@mailbox(capacity: 2048) lands on Handler.attrs with span anchored to the @.

Summary by cubic

Keep attribute arguments and handler attributes in the AST. This lets tools and analyzers read attribute payloads and source spans.

  • New Features

    • Attribute now includes args: Vec<AttrArg> and a span that covers @ through the closing ).
    • AttrArg supports Ident, Named, Assigned, Call, and Expr per §16; the parser canonicalizes = and call forms after parsing.
    • Handler attrs are preserved: Actor.handlers now hold Handler { attrs, function } instead of bare Function.
  • Migration

    • Update usages of Actor.handlers: Vec<Function> to Vec<Handler>; access the function via handler.function and its attrs via handler.attrs.
    • If you read attributes, use Attribute.args and Attribute.span instead of assuming no arguments.

Written for commit b34be49. Summary will update on new commits.

Review in cubic

`@mailbox(capacity: 2048)` and `@supervisor(strategy: "one_for_one")`
lost their arguments at parse time — the parser skipped everything
inside the parens — and attributes on actor handlers were dropped
outright. Nothing downstream (semantic analysis, diagnostics) could
ever see them.

`Attribute` now carries `args: Vec<AttrArg>` plus a span covering `@`
through the closing paren, with `AttrArg` mirroring the §16 grammar
(`attr_arg = IDENT [ ":" expr | "=" expr | "(" expr ")" ] | expr`).
Only the `IDENT ":"` form needs lookahead; the `=` and call forms are
valid expressions, so they parse as expressions and canonicalize to the
most specific attr shape afterwards.

Actor handlers become `Handler { attrs, function }`, since a handler is
a `fn_def` and §16 puts attrs on `fn_def` itself. The now-unused
`skip_balanced_after_open` helper is removed.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Src as Source Text
    participant Parser as Parser
    participant Tokenizer as Token Stream
    participant AST as AST Node
    participant Client as API Consumer

    Note over Src,Client: Attribute Parsing Flow

    Src->>Tokenizer: Lex tokens
    Tokenizer-->>Parser: Token stream

    Parser->>Parser: attrs() method
    Note over Parser: Reads @ token, expects ident
    
    alt @IDENT with arguments
        Parser->>Parser: Match LParen
        Parser->>Parser: attr_args() loop
        loop For each arg
            Parser->>Parser: Lookahead for IDENT + Colon
            alt IDENT followed by Colon
                Parser->>AST: Create AttrArg::Named { name, value }
            else Other expression forms
                Parser->>Parser: Parse as delimited_expr()
                Parser->>Parser: classify_attr_expr() canonicalization
                alt Single ident path
                    Parser->>AST: Create AttrArg::Ident
                else Assignment expression
                    Parser->>AST: Create AttrArg::Assigned
                else Single-arg call expression
                    Parser->>AST: Create AttrArg::Call
                else Other expression
                    Parser->>AST: Create AttrArg::Expr
                end
            end
            opt Comma separator
                Parser->>Parser: Eat comma, continue loop
            end
        end
        Parser->>Parser: Expect RParen
        Parser->>AST: Store args, compute span (@ to )
    else @IDENT without arguments
        Parser->>AST: Store empty args, span covers name only
    end

    Parser->>AST: Return Attribute { name, args, span }

    Note over Parser,AST: Actor Handler Parsing Flow

    Parser->>Parser: Actor body: handlers loop
    loop For each handler element
        Parser->>Parser: Skip doc comments
        Parser->>Parser: Parse attrs() for handler
        alt @mailbox or other handler attrs present
            Parser->>AST: Collect Vec{Attribute} for handler
        else No handler attrs
            Parser->>AST: Empty vec
        end
        Parser->>Parser: Parse visibility & async
        alt fn keyword found
            Parser->>Parser: Parse function_after_mods()
            Parser->>AST: Create Handler { attrs, function }
        else Field declaration
            Parser->>Parser: Parse ident : type
            Note over Parser: Handler attrs before fields are discarded (per spec)
        end
    end

    Parser->>AST: Return Actor with Vec{Handler}

    Note over AST,Client: Consumer Usage

    Client->>AST: Access attribute args
    alt Named args pattern
        Client->>Client: Match AttrArg::Named for configuration
    else Ident pattern
        Client->>Client: Match AttrArg::Ident for derive list
    end

    Client->>AST: Access handler attributes
    Client->>Client: Read handler.attrs for mailbox config
    
    opt Backward compatibility
        Client->>Client: Access handler.function for signature
    end
Loading

Auto-approved: Preserves attribute arguments and handler attrs in AST; adds AttrArg enum and Handler struct. Low risk: purely additive, well-tested, and no behavior change.

Re-trigger cubic

@StreamDemon StreamDemon merged commit f3b173a into feature/parser-enhancements-base Jul 2, 2026
2 checks passed
@StreamDemon StreamDemon deleted the feature/parser-enh-attr-args branch July 2, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant