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 #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).
This commit is contained in:
parent
18840b0719
commit
0df5ac8269
6 changed files with 41 additions and 5 deletions
|
@ -1328,14 +1328,30 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||
return placeholder;
|
||||
}
|
||||
|
||||
// The only way that we can end up with a `MacCall` expression statement,
|
||||
// (as opposed to a `StmtKind::MacCall`) is if we have a macro as the
|
||||
// traiing expression in a block (e.g. `fn foo() { my_macro!() }`).
|
||||
// Record this information, so that we can report a more specific
|
||||
// `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
|
||||
// See #78991 for an investigation of treating macros in this position
|
||||
// as statements, rather than expressions, during parsing.
|
||||
if let StmtKind::Expr(expr) = &stmt.kind {
|
||||
if matches!(**expr, ast::Expr { kind: ast::ExprKind::MacCall(..), .. }) {
|
||||
self.cx.current_expansion.is_trailing_mac = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The placeholder expander gives ids to statements, so we avoid folding the id here.
|
||||
// We don't use `assign_id!` - it will be called when we visit statement's contents
|
||||
// (e.g. an expression, item, or local)
|
||||
let ast::Stmt { id, kind, span } = stmt;
|
||||
noop_flat_map_stmt_kind(kind, self)
|
||||
let res = noop_flat_map_stmt_kind(kind, self)
|
||||
.into_iter()
|
||||
.map(|kind| ast::Stmt { id, kind, span })
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
self.cx.current_expansion.is_trailing_mac = false;
|
||||
res
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, block: &mut P<Block>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue