-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Parse bang macro as a statement when used in trailing expr position #78991
Conversation
cc rust-lang#33953 Currently, the following code produces an error ```rust fn main() { macro_rules! a { ($e:expr) => { $e; } } a!(true) } ``` With this change, it now compiles, since we parse `a!(true)` as a statement.
(rust_highfive has picked a reviewer for you, use r? to override) |
@bors try |
⌛ Trying commit 6a5caac with merge 8b72f65b805cbe4c261de45030d663f524f27376... |
☀️ Try build successful - checks-actions |
@craterbot check |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
There's a large number of legitimate regressions, due to some trailing macro expressions now evaluating to It should be possible to make this a warning - we can continue to parse trailing macro calls as expressions, but add a flag that makes us emit a warning during typecheck if the type of the expression is not However, it might be worthwhile to check with the lang team to make sure this is something that we want to do. |
On further investigation, many (if not all) of the regressions appear to be due to an interaction with semicolons in macro bodies (see #78685) If we have a macro like: macro_rules! a {
($e:expr) => { $e; }
} we ignore the trailing semicolon if the macro is used in expression position: rust/compiler/rustc_expand/src/mbe/macro_rules.rs Lines 151 to 156 in c919f49
With this change, we now parse the macro body in statement position, so the above code is never run. To make this into a warning, we would want to extend the above workaround to apply to statements, but only when the original macro call occurred as the trailing statement in a block. |
Yes, I think it would be a good strategy for the last statement of expanded code to inherit "expr-ness" from the macro call for the purpose of this hack. I don't have any other specific ideas right now, but I think the general guiding principle here is that expansion should be built and work using a holistic token-based model as if we didn't have any compatibility restrictions. Then minimal compatibility hack would be applied on top of that model in such way that they could report warnings or be turned off at edition boundary. |
@Aaron1011 Ping from triage: Any updates on this? |
I'll revisit this after #79819 is merged |
Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR rust-lang#78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
…=petrochenkov Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR rust-lang#78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
Closing this as inactive |
cc #33953
Currently, the following code produces an error
With this change, it now compiles, since we parse
a!(true)
as astatement.