Sitelet https://github.com/argotorg/fe/pull/1523
Skip to content

fix(fmt): preserve syntax across formatting round trips - #1523

Draft
cburgdorf wants to merge 8 commits into
argotorg:masterfrom
cburgdorf:fe_fmt_fix
Draft

cburgdorf wants to merge 8 commits into
argotorg:masterfrom
cburgdorf:fe_fmt_fix

Conversation

@cburgdorf

Copy link
Copy Markdown
Collaborator

Keep line breaks after parser-ambiguous operators, separate nested qualified-type angle brackets, and retain restricted visibility modifiers.

Handle item-leading trivia without accumulating blank lines, and add corpus round-trip and regression coverage for reparsing and idempotence.

@cburgdorf
cburgdorf force-pushed the fe_fmt_fix branch 4 times, most recently from 308d519 to 22bdb58 Compare September 29, 2026 22:03
@micahscopes

Copy link
Copy Markdown
Collaborator

Thanks for this PR. I added a few commits on top of yours, without changing your commit, for cases where fe fmt still changed code. They are also on master, so they can move to a separate PR if you'd rather keep this one small.

  • Keep attributes on for loops. #[unroll(never)] was deleted.
  • Keep inner attributes of nested modules. #![arithmetic(unchecked)] inside mod m { ... } was deleted, which changes how that module's arithmetic behaves. File-level handling is unchanged.
  • Keep comments among an item's attributes. A // line between doc comments and attributes was deleted (one is in std/src/abi/sol.fe). A comment right after the attributes now stays on its own line.
  • Write Some(T) instead of Some(T,) in enum variants and matching patterns.
  • Tests. A fixture for pub(ingot) / pub(super) on ordinary items, since the widened pub still parses and would otherwise pass unnoticed. The corpus test now also compares each file's tokens before and after formatting (ignoring whitespace and separator commas); on master's formatter it fails on the cases above and on the widened visibility. Loop attributes and nested inner attributes are inline tests, because the tree-sitter grammar doesn't accept them in the fixtures directory yet.
  • Release note. One sentence about the visibility and attribute fixes.

Checked with the full test suite and clippy, each commit building on its own, and a merge into current master with the formatter tests passing there. Formatting every .fe file in the repository changes no diagnostics or tokens, and a second pass changes nothing. The only formatted files tree-sitter rejects are four with Foo<<T as K>::C>, which the compiler accepts; that looks like a grammar gap rather than a formatter one.

Feel free to reshape, squash or drop any of these.

@cburgdorf
cburgdorf force-pushed the fe_fmt_fix branch 2 times, most recently from 33a33b5 to e0c6059 Compare October 4, 2026 22:37
cburgdorf and others added 8 commits October 6, 2026 22:41
Keep line breaks after parser-ambiguous operators, separate nested qualified-type angle brackets, and retain restricted visibility modifiers.

Handle item-leading trivia without accumulating blank lines, and add corpus round-trip and regression coverage for reparsing and idempotence.
Master keeps attributes on for loops now; this adds cases it does not
cover: several attributes stacked on one loop, attributes on a nested
loop, and irregular spacing in the loop header.
…tributes

Inner attributes of a nested module (`#![...]`) were printed from a
separate path that dropped the blank line between them and the first
item. Render them like other attribute lists instead: keep the line
breaks that follow them, and print `#![` for inner attributes in
`NormalAttr`. This also lets the next change keep comments placed among
inner attributes.
An attribute list was formatted from its attributes only, so a plain
comment between doc comments and attributes was deleted (as in
`std/src/abi/sol.fe`).

A comment right after the attribute list belongs to the item node, so it
sent the whole item through the comment-preserving fallback, which
printed it with a leading space. Such comments are now kept with the
attribute list, one per line, and the item is formatted as usual.
The one-element tuple rule also applied to tuple variants, so `Some(T)`
and the pattern `Some(x)` were written as `Some(T,)` and `Some(x,)`.
Only a one-element tuple type or pattern needs that comma.
`pub(ingot)` and `pub(super)` still parse after being widened to `pub`,
so only a snapshot catches that regression. Cover functions, structs,
fields, impl functions, consts, enums, traits, type aliases, modules,
`use` and extern functions, with and without comments inside the item.
The corpus test only checked that formatted files parse and are stable,
which passes when the formatter deletes an attribute, a comment or a
visibility restriction. Also compare the tokens of each file before and
after formatting, ignoring whitespace, separator commas and the braces
added around `return` match arm bodies.
The visibility, for loop attribute and inner attribute fixes already ship
from master with their own entry, so only mention what this branch adds.

This branch has not been deployed

No deployments
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.

2 participants