diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 3d5bc770c4fb0..b454737fb8077 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -929,6 +929,7 @@ pub struct ExpansionData { pub prior_type_ascription: Option<(Span, bool)>, /// Some parent node that is close to this macro call pub lint_node_id: NodeId, + pub is_trailing_mac: bool, } type OnExternModLoaded<'a> = @@ -979,6 +980,7 @@ impl<'a> ExtCtxt<'a> { dir_ownership: DirOwnership::Owned { relative: None }, prior_type_ascription: None, lint_node_id: ast::CRATE_NODE_ID, + is_trailing_mac: false, }, force_mode: false, expansions: FxHashMap::default(), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c72b1b33dbc85..09beda3348374 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -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) { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 7f985af364d7d..b97593b92b355 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -43,6 +43,7 @@ crate struct ParserAnyMacro<'a> { /// The ident of the macro we're parsing macro_ident: Ident, lint_node_id: NodeId, + is_trailing_mac: bool, arm_span: Span, } @@ -116,8 +117,14 @@ fn emit_frag_parse_err( impl<'a> ParserAnyMacro<'a> { crate fn make(mut self: Box>, kind: AstFragmentKind) -> AstFragment { - let ParserAnyMacro { site_span, macro_ident, ref mut parser, lint_node_id, arm_span } = - *self; + let ParserAnyMacro { + site_span, + macro_ident, + ref mut parser, + lint_node_id, + arm_span, + is_trailing_mac, + } = *self; let snapshot = &mut parser.clone(); let fragment = match parse_ast_fragment(parser, kind) { Ok(f) => f, @@ -131,11 +138,12 @@ impl<'a> ParserAnyMacro<'a> { // `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`, // but `m!()` is allowed in expression positions (cf. issue #34706). if kind == AstFragmentKind::Expr && parser.token == token::Semi { - parser.sess.buffer_lint( + parser.sess.buffer_lint_with_diagnostic( SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, parser.token.span, lint_node_id, "trailing semicolon in macro used in expression position", + BuiltinLintDiagnostics::TrailingMacro(is_trailing_mac, macro_ident), ); parser.bump(); } @@ -301,6 +309,7 @@ fn generic_extension<'cx>( site_span: sp, macro_ident: name, lint_node_id: cx.current_expansion.lint_node_id, + is_trailing_mac: cx.current_expansion.is_trailing_mac, arm_span, }); } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 31ce77375e539..ad8a41a56cc16 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -744,6 +744,12 @@ pub trait LintContext: Sized { &format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`") ); } + BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => { + if is_trailing { + db.note("macro invocations at the end of a block are treated as expressions"); + db.note(&format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`")); + } + } } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ec17e7a6b0373..6c38b8f5bc0a1 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -303,6 +303,7 @@ pub enum BuiltinLintDiagnostics { ProcMacroBackCompat(String), OrPatternsBackCompat(Span, String), ReservedPrefix(Span), + TrailingMacro(bool, Ident), } /// Lints that are buffered up early on in the `Session` before the diff --git a/src/test/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr b/src/test/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr index c00c3d77dcedc..84ad32bddd55b 100644 --- a/src/test/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr +++ b/src/test/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr @@ -14,6 +14,8 @@ LL | #![warn(semicolon_in_expressions_from_macros)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79813 + = note: macro invocations at the end of a block are treated as expressions + = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo` = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) warning: trailing semicolon in macro used in expression position