-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix parenthesization of subexprs containing statement boundary #119105
Conversation
(rustbot has picked a reviewer for you, use r? to override) |
I'm surprised that Will review the code a bit later. |
For But there is only one AST pretty-printer, and |
@bors r+ |
☀️ Test successful - checks-actions |
Finished benchmarking commit (89e2160): comparison URL. Overall result: no relevant changes - no action needed@rustbot label: -perf-regression Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 672.366s -> 670.933s (-0.21%) |
Fix, document, and test parser and pretty-printer edge cases related to braced macro calls _Review note: this is a deceptively small PR because it comes with 145 lines of docs and 196 lines of tests, and only 25 lines of compiler code changed. However, I recommend reviewing it 1 commit at a time because much of the effect of the code changes is non-local i.e. affecting code that is not visible in the final state of the PR. I have paid attention that reviewing the PR one commit at a time is as easy as I can make it. All of the code you need to know about is touched in those commits, even if some of those changes disappear by the end of the stack._ This is a follow-up to rust-lang#119105. One case that is not relevant to `-Zunpretty=expanded`, but which came up as I'm porting rust-lang#119105 and rust-lang#118726 into `syn`'s printer and `prettyplease`'s printer where it **is** relevant, and is also relevant to rustc's `stringify!`, is statement boundaries in the vicinity of braced macro calls. Rustc's AST pretty-printer produces invalid syntax for statements that begin with a braced macro call: ```rust macro_rules! stringify_item { ($i:item) => { stringify!($i) }; } macro_rules! repro { ($e:expr) => { stringify_item!(fn main() { $e + 1; }) }; } fn main() { println!("{}", repro!(m! {})); } ``` **Before this PR:** output is not valid Rust syntax. ```console fn main() { m! {} + 1; } ``` ```console error: leading `+` is not supported --> <anon>:1:19 | 1 | fn main() { m! {} + 1; } | ^ unexpected `+` | help: try removing the `+` | 1 - fn main() { m! {} + 1; } 1 + fn main() { m! {} 1; } | ``` **After this PR:** valid syntax. ```console fn main() { (m! {}) + 1; } ```
This PR fixes a multitude of false negatives and false positives in the AST pretty printer's parenthesis insertion related to statement boundaries — statements which terminate unexpectedly early if there aren't parentheses.
Without this fix, the AST pretty printer (including both
stringify!
andrustc -Zunpretty=expanded
) is prone to producing output which is not syntactically valid Rust. Invalid output is problematic because it means Rustfmt is unable to parse the output ofcargo expand
, for example, causing friction by forcing someone trying to debug a macro into reading poorly formatted code.I believe the set of bugs fixed in this PR account for the most prevalent reason that
cargo expand
produces invalid output in real-world usage.Fixes #98790.
False negatives
The following is a correct program —
cargo check
succeeds.But
rustc -Zunpretty=expanded main.rs
produces output that is invalid Rust syntax, because parenthesization is needed and not being done by the pretty printer.Piping this expanded code to rustfmt, it fails to parse.
Fixed output after this PR:
False positives
Less problematic, but worth fixing (just like #118726).
Output of
rustc -Zunpretty=expanded lib.rs
before this PR. There is no reason parentheses would need to be inserted there.After this PR:
Alternatives considered
In this PR I opted to parenthesize only the leading subexpression causing the statement boundary, rather than the entire statement. Example:
This PR produces the following pretty-printed contents for fn main:
A different equally correct output would be:
I chose the one I did because it is the only approach used by handwritten code in the standard library and compiler. There are 4 places where parenthesization is being used to prevent a statement boundary, and in all 4, the developer has chosen to parenthesize the smallest subexpression rather than the whole statement:
rust/compiler/rustc_codegen_cranelift/example/alloc_system.rs
Line 102 in b37d43e
rust/compiler/rustc_parse/src/errors.rs
Lines 1021 to 1029 in b37d43e
rust/library/core/src/future/poll_fn.rs
Line 151 in b37d43e
rust/library/core/src/ops/range.rs
Lines 824 to 828 in b37d43e