diff --git a/src/librustc_builtin_macros/source_util.rs b/src/librustc_builtin_macros/source_util.rs index 67145c6bf433b..1b164eae5a345 100644 --- a/src/librustc_builtin_macros/source_util.rs +++ b/src/librustc_builtin_macros/source_util.rs @@ -122,6 +122,7 @@ pub fn expand_include<'cx>( struct ExpandResult<'a> { p: Parser<'a>, + node_id: ast::NodeId, } impl<'a> base::MacResult for ExpandResult<'a> { fn make_expr(mut self: Box>) -> Option> { @@ -130,7 +131,7 @@ pub fn expand_include<'cx>( self.p.sess.buffer_lint( &INCOMPLETE_INCLUDE, self.p.token.span, - ast::CRATE_NODE_ID, + self.node_id, "include macro expected single expression in source", ); } @@ -158,7 +159,7 @@ pub fn expand_include<'cx>( } } - Box::new(ExpandResult { p }) + Box::new(ExpandResult { p, node_id: cx.resolver.lint_node_id(cx.current_expansion.id) }) } // include_str! : read the given file, insert it as a literal string expr diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs index 13637e58c9364..a57ae798ffceb 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -915,6 +915,9 @@ pub trait Resolver { fn check_unused_macros(&mut self); + /// Some parent node that is close enough to the given macro call. + fn lint_node_id(&mut self, expn_id: ExpnId) -> NodeId; + fn has_derive_copy(&self, expn_id: ExpnId) -> bool; fn add_derive_copy(&mut self, expn_id: ExpnId); fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result; diff --git a/src/librustc_expand/mbe/macro_check.rs b/src/librustc_expand/mbe/macro_check.rs index 582c26162ed6d..ca3e68fa6706e 100644 --- a/src/librustc_expand/mbe/macro_check.rs +++ b/src/librustc_expand/mbe/macro_check.rs @@ -106,7 +106,7 @@ //! bound. use crate::mbe::{KleeneToken, TokenTree}; -use rustc_ast::ast::NodeId; +use rustc_ast::ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast::token::{DelimToken, Token, TokenKind}; use rustc_data_structures::fx::FxHashMap; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; @@ -626,5 +626,8 @@ fn ops_is_prefix( } fn buffer_lint(sess: &ParseSess, span: MultiSpan, node_id: NodeId, message: &str) { - sess.buffer_lint(&META_VARIABLE_MISUSE, span, node_id, message); + // Macros loaded from other crates have dummy node ids. + if node_id != DUMMY_NODE_ID { + sess.buffer_lint(&META_VARIABLE_MISUSE, span, node_id, message); + } } diff --git a/src/librustc_expand/mbe/macro_parser.rs b/src/librustc_expand/mbe/macro_parser.rs index 0cf092d912bcf..db8258a77863b 100644 --- a/src/librustc_expand/mbe/macro_parser.rs +++ b/src/librustc_expand/mbe/macro_parser.rs @@ -383,7 +383,7 @@ fn nameize>( } } TokenTree::MetaVarDecl(span, _, id) if id.name == kw::Invalid => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span) { + if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { return Err((span, "missing fragment specifier".to_string())); } } @@ -566,7 +566,7 @@ fn inner_parse_loop<'root, 'tt>( // We need to match a metavar (but the identifier is invalid)... this is an error TokenTree::MetaVarDecl(span, _, id) if id.name == kw::Invalid => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span) { + if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { return Error(span, "missing fragment specifier".to_string()); } } diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs index ecadf320f87c4..8cdb5b09c9e8b 100644 --- a/src/librustc_expand/mbe/macro_rules.rs +++ b/src/librustc_expand/mbe/macro_rules.rs @@ -474,7 +474,9 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = mbe::quoted::parse(tt.clone().into(), true, sess).pop().unwrap(); + let tt = mbe::quoted::parse(tt.clone().into(), true, sess, def.id) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; } @@ -491,7 +493,9 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return mbe::quoted::parse(tt.clone().into(), false, sess).pop().unwrap(); + return mbe::quoted::parse(tt.clone().into(), false, sess, def.id) + .pop() + .unwrap(); } } sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") @@ -509,9 +513,7 @@ pub fn compile_declarative_macro( valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); } - // We use CRATE_NODE_ID instead of `def.id` otherwise we may emit buffered lints for a node id - // that is not lint-checked and trigger the "failed to process buffered lint here" bug. - valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses); + valid &= macro_check::check_meta_variables(sess, def.id, def.span, &lhses, &rhses); let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules); match transparency_error { diff --git a/src/librustc_expand/mbe/quoted.rs b/src/librustc_expand/mbe/quoted.rs index 3295f5b392d6d..de66c2ada40e6 100644 --- a/src/librustc_expand/mbe/quoted.rs +++ b/src/librustc_expand/mbe/quoted.rs @@ -1,6 +1,7 @@ use crate::mbe::macro_parser; use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree}; +use rustc_ast::ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; use rustc_ast_pretty::pprust; @@ -36,6 +37,7 @@ pub(super) fn parse( input: tokenstream::TokenStream, expect_matchers: bool, sess: &ParseSess, + node_id: NodeId, ) -> Vec { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -46,7 +48,7 @@ pub(super) fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess); + let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -65,7 +67,10 @@ pub(super) fn parse( } tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp), }; - sess.missing_fragment_specifiers.borrow_mut().insert(span); + if node_id != DUMMY_NODE_ID { + // Macros loaded from other crates have dummy node ids. + sess.missing_fragment_specifiers.borrow_mut().insert(span, node_id); + } result.push(TokenTree::MetaVarDecl(span, ident, Ident::invalid())); } @@ -96,6 +101,7 @@ fn parse_tree( trees: &mut impl Iterator, expect_matchers: bool, sess: &ParseSess, + node_id: NodeId, ) -> TokenTree { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { @@ -111,7 +117,7 @@ fn parse_tree( sess.span_diagnostic.span_err(span.entire(), &msg); } // Parse the contents of the sequence itself - let sequence = parse(tts, expect_matchers, sess); + let sequence = parse(tts, expect_matchers, sess, node_id); // Get the Kleene operator and optional separator let (separator, kleene) = parse_sep_and_kleene_op(trees, span.entire(), sess); // Count the number of captured "names" (i.e., named metavars) @@ -158,7 +164,7 @@ fn parse_tree( // descend into the delimited set and further parse it. tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, - Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess) }), + Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess, node_id) }), ), } } diff --git a/src/librustc_hir/definitions.rs b/src/librustc_hir/definitions.rs index 2dd5e27ead265..b63dd653c4dd8 100644 --- a/src/librustc_hir/definitions.rs +++ b/src/librustc_hir/definitions.rs @@ -519,6 +519,12 @@ impl Definitions { let old_index = self.placeholder_field_indices.insert(node_id, index); assert!(old_index.is_none(), "placeholder field index is reset for a node ID"); } + + pub fn lint_node_id(&mut self, expn_id: ExpnId) -> ast::NodeId { + self.invocation_parents + .get(&expn_id) + .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[*id]) + } } impl DefPathData { diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 9a60e74d94d01..1a9bf4e1e8f3d 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -307,16 +307,21 @@ fn configure_and_expand_inner<'a>( ecx.check_unused_macros(); }); - let mut missing_fragment_specifiers: Vec<_> = - ecx.parse_sess.missing_fragment_specifiers.borrow().iter().cloned().collect(); - missing_fragment_specifiers.sort(); + let mut missing_fragment_specifiers: Vec<_> = ecx + .parse_sess + .missing_fragment_specifiers + .borrow() + .iter() + .map(|(span, node_id)| (*span, *node_id)) + .collect(); + missing_fragment_specifiers.sort_unstable_by_key(|(span, _)| *span); let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); - for span in missing_fragment_specifiers { + for (span, node_id) in missing_fragment_specifiers { let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; let msg = "missing fragment specifier"; - resolver.lint_buffer().buffer_lint(lint, ast::CRATE_NODE_ID, span, msg); + resolver.lint_buffer().buffer_lint(lint, node_id, span, msg); } if cfg!(windows) { env::set_var("PATH", &old_path); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 394d8dc4e1135..1b49722355e54 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -288,7 +288,8 @@ impl<'a> base::Resolver for Resolver<'a> { // Derives are not included when `invocations` are collected, so we have to add them here. let parent_scope = &ParentScope { derives, ..parent_scope }; - let (ext, res) = self.smart_resolve_macro_path(path, kind, parent_scope, force)?; + let node_id = self.lint_node_id(eager_expansion_root); + let (ext, res) = self.smart_resolve_macro_path(path, kind, parent_scope, node_id, force)?; let span = invoc.span(); invoc_id.set_expn_data(ext.expn_data( @@ -338,6 +339,10 @@ impl<'a> base::Resolver for Resolver<'a> { } } + fn lint_node_id(&mut self, expn_id: ExpnId) -> NodeId { + self.definitions.lint_node_id(expn_id) + } + fn has_derive_copy(&self, expn_id: ExpnId) -> bool { self.containers_deriving_copy.contains(&expn_id) } @@ -390,6 +395,7 @@ impl<'a> Resolver<'a> { path: &ast::Path, kind: MacroKind, parent_scope: &ParentScope<'a>, + node_id: NodeId, force: bool, ) -> Result<(Lrc, Res), Indeterminate> { let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force) @@ -430,7 +436,7 @@ impl<'a> Resolver<'a> { _ => panic!("expected `DefKind::Macro` or `Res::NonMacroAttr`"), }; - self.check_stability_and_deprecation(&ext, path); + self.check_stability_and_deprecation(&ext, path, node_id); Ok(if ext.macro_kind() != kind { let expected = kind.descr_expected(); @@ -984,13 +990,17 @@ impl<'a> Resolver<'a> { } } - fn check_stability_and_deprecation(&mut self, ext: &SyntaxExtension, path: &ast::Path) { + fn check_stability_and_deprecation( + &mut self, + ext: &SyntaxExtension, + path: &ast::Path, + node_id: NodeId, + ) { let span = path.span; if let Some(stability) = &ext.stability { if let StabilityLevel::Unstable { reason, issue, is_soft } = stability.level { let feature = stability.feature; if !self.active_features.contains(&feature) && !span.allows_unstable(feature) { - let node_id = ast::CRATE_NODE_ID; let lint_buffer = &mut self.lint_buffer; let soft_handler = |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg); diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index bb0d6e1a47ead..58388bafbeddf 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -606,6 +606,7 @@ declare_lint_pass! { INLINE_NO_SANITIZE, ASM_SUB_REGISTER, UNSAFE_OP_IN_UNSAFE_FN, + INCOMPLETE_INCLUDE, ] } diff --git a/src/librustc_session/parse.rs b/src/librustc_session/parse.rs index 233761dbed7de..ddbc95fb1b0b8 100644 --- a/src/librustc_session/parse.rs +++ b/src/librustc_session/parse.rs @@ -119,7 +119,7 @@ pub struct ParseSess { pub unstable_features: UnstableFeatures, pub config: CrateConfig, pub edition: Edition, - pub missing_fragment_specifiers: Lock>, + pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. pub raw_identifier_spans: Lock>, /// Used to determine and report recursive module inclusions. @@ -150,7 +150,7 @@ impl ParseSess { unstable_features: UnstableFeatures::from_environment(), config: FxHashSet::default(), edition: ExpnId::root().expn_data().edition, - missing_fragment_specifiers: Lock::new(FxHashSet::default()), + missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), included_mod_stack: Lock::new(vec![]), source_map, diff --git a/src/test/ui/lint/expansion-time-include.rs b/src/test/ui/lint/expansion-time-include.rs new file mode 100644 index 0000000000000..4ea89d5adff94 --- /dev/null +++ b/src/test/ui/lint/expansion-time-include.rs @@ -0,0 +1,4 @@ +// ignore-test auxiliary file for expansion-time.rs + +1 +2 diff --git a/src/test/ui/lint/expansion-time.rs b/src/test/ui/lint/expansion-time.rs new file mode 100644 index 0000000000000..6e420c51f0a7f --- /dev/null +++ b/src/test/ui/lint/expansion-time.rs @@ -0,0 +1,23 @@ +// check-pass + +#[warn(meta_variable_misuse)] +macro_rules! foo { + ( $($i:ident)* ) => { $($i)+ }; //~ WARN meta-variable repeats with different Kleene operator +} + +#[warn(missing_fragment_specifier)] +macro_rules! m { ($i) => {} } //~ WARN missing fragment specifier + //~| WARN this was previously accepted + +#[warn(soft_unstable)] +mod benches { + #[bench] //~ WARN use of unstable library feature 'test' + //~| WARN this was previously accepted + fn foo() {} +} + +#[warn(incomplete_include)] +fn main() { + // WARN see in the stderr file, the warning points to the included file. + include!("expansion-time-include.rs"); +} diff --git a/src/test/ui/lint/expansion-time.stderr b/src/test/ui/lint/expansion-time.stderr new file mode 100644 index 0000000000000..e6b5cf67e3904 --- /dev/null +++ b/src/test/ui/lint/expansion-time.stderr @@ -0,0 +1,56 @@ +warning: meta-variable repeats with different Kleene operator + --> $DIR/expansion-time.rs:5:29 + | +LL | ( $($i:ident)* ) => { $($i)+ }; + | - ^^ - conflicting repetition + | | + | expected repetition + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:3:8 + | +LL | #[warn(meta_variable_misuse)] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: missing fragment specifier + --> $DIR/expansion-time.rs:9:19 + | +LL | macro_rules! m { ($i) => {} } + | ^^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:8:8 + | +LL | #[warn(missing_fragment_specifier)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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 #40107 + +warning: use of unstable library feature 'test': `bench` is a part of custom test frameworks which are unstable + --> $DIR/expansion-time.rs:14:7 + | +LL | #[bench] + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:12:8 + | +LL | #[warn(soft_unstable)] + | ^^^^^^^^^^^^^ + = 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 #64266 + +warning: include macro expected single expression in source + --> $DIR/expansion-time-include.rs:4:1 + | +LL | 2 + | ^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:19:8 + | +LL | #[warn(incomplete_include)] + | ^^^^^^^^^^^^^^^^^^ + +warning: 4 warnings emitted +