From 69b71e44107b4905ec7ad84ccb3edf4f14b3df69 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 18 Jul 2025 00:10:46 +0800 Subject: Mitigate `#[align]` name resolution ambiguity regression with a rename From `#[align]` -> `#[rustc_align]`. Attributes starting with `rustc` are always perma-unstable and feature-gated by `feature(rustc_attrs)`. See regression RUST-143834. For the underlying problem where even introducing new feature-gated unstable built-in attributes can break user code such as ```rs macro_rules! align { () => { /* .. */ }; } pub(crate) use align; // `use` here becomes ambiguous ``` refer to RUST-134963. Since the `#[align]` attribute is still feature-gated by `feature(fn_align)`, we can rename it as a mitigation. Note that `#[rustc_align]` will obviously mean that current unstable user code using `feature(fn_aling)` will need additionally `feature(rustc_attrs)`, but this is a short-term mitigation to buy time, and is expected to be changed to a better name with less collision potential. See where mitigation options were considered. --- compiler/rustc_parse/src/validate_attr.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index a476f0db37e..cca621103b5 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -294,7 +294,9 @@ pub fn check_builtin_meta_item( | sym::rustc_paren_sugar | sym::type_const | sym::repr - | sym::align + // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres + // ambiguity + | sym::rustc_align | sym::deprecated | sym::optimize | sym::pointee -- cgit 1.4.1-3-g733a5 From 2832517ba174906b6d6ebedf4893dca8d8a98f55 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Mon, 21 Jul 2025 11:05:37 +0800 Subject: Clean code for `rustc_parse/src/lexer` 1. Rename `make_unclosed_delims_error` and return `Vec` 2. change magic number `unclosed_delimiter_show_limit` to const 3. move `eof_err` below parsing logic 4. Add `calculate_spacing` for `bump` and `bump_minimal` Signed-off-by: xizheyin --- compiler/rustc_parse/src/lexer/diagnostics.rs | 44 ++++++----- compiler/rustc_parse/src/lexer/mod.rs | 18 ++--- compiler/rustc_parse/src/lexer/tokentrees.rs | 106 +++++++++++++------------- 3 files changed, 83 insertions(+), 85 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index 6de001fc998..947f3df179f 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -126,23 +126,29 @@ pub(super) fn report_suspicious_mismatch_block( } } -pub(crate) fn make_unclosed_delims_error( - unmatched: UnmatchedDelim, - psess: &ParseSess, -) -> Option> { - // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to - // `unmatched_delims` only for error recovery in the `Parser`. - let found_delim = unmatched.found_delim?; - let mut spans = vec![unmatched.found_span]; - if let Some(sp) = unmatched.unclosed_span { - spans.push(sp); - }; - let err = psess.dcx().create_err(MismatchedClosingDelimiter { - spans, - delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(), - unmatched: unmatched.found_span, - opening_candidate: unmatched.candidate_span, - unclosed: unmatched.unclosed_span, - }); - Some(err) +pub(crate) fn make_errors_for_mismatched_closing_delims<'psess>( + unmatcheds: &[UnmatchedDelim], + psess: &'psess ParseSess, +) -> Vec> { + unmatcheds + .iter() + .filter_map(|unmatched| { + // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to + // `unmatched_delims` only for error recovery in the `Parser`. + let found_delim = unmatched.found_delim?; + let mut spans = vec![unmatched.found_span]; + if let Some(sp) = unmatched.unclosed_span { + spans.push(sp); + }; + let err = psess.dcx().create_err(MismatchedClosingDelimiter { + spans, + delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()) + .to_string(), + unmatched: unmatched.found_span, + opening_candidate: unmatched.candidate_span, + unclosed: unmatched.unclosed_span, + }); + Some(err) + }) + .collect() } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 60d275bf2b4..85af5a615ae 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,4 +1,4 @@ -use diagnostics::make_unclosed_delims_error; +use diagnostics::make_errors_for_mismatched_closing_delims; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; @@ -71,27 +71,23 @@ pub(crate) fn lex_token_trees<'psess, 'src>( }; let res = lexer.lex_token_trees(/* is_delimited */ false); - let mut unmatched_delims: Vec<_> = lexer - .diag_info - .unmatched_delims - .into_iter() - .filter_map(|unmatched_delim| make_unclosed_delims_error(unmatched_delim, psess)) - .collect(); + let mut unmatched_closing_delims: Vec<_> = + make_errors_for_mismatched_closing_delims(&lexer.diag_info.unmatched_delims, psess); match res { Ok((_open_spacing, stream)) => { - if unmatched_delims.is_empty() { + if unmatched_closing_delims.is_empty() { Ok(stream) } else { // Return error if there are unmatched delimiters or unclosed delimiters. - Err(unmatched_delims) + Err(unmatched_closing_delims) } } Err(errs) => { // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch // because the delimiter mismatch is more likely to be the root cause of error - unmatched_delims.extend(errs); - Err(unmatched_delims) + unmatched_closing_delims.extend(errs); + Err(unmatched_closing_delims) } } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 64748199f28..634f4c30b26 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -51,45 +51,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } - fn eof_err(&mut self) -> Diag<'psess> { - let msg = "this file contains an unclosed delimiter"; - let mut err = self.dcx().struct_span_err(self.token.span, msg); - - let unclosed_delimiter_show_limit = 5; - let len = usize::min(unclosed_delimiter_show_limit, self.diag_info.open_delimiters.len()); - for &(_, span) in &self.diag_info.open_delimiters[..len] { - err.span_label(span, "unclosed delimiter"); - self.diag_info.unmatched_delims.push(UnmatchedDelim { - found_delim: None, - found_span: self.token.span, - unclosed_span: Some(span), - candidate_span: None, - }); - } - - if let Some((_, span)) = self.diag_info.open_delimiters.get(unclosed_delimiter_show_limit) - && self.diag_info.open_delimiters.len() >= unclosed_delimiter_show_limit + 2 - { - err.span_label( - *span, - format!( - "another {} unclosed delimiters begin from here", - self.diag_info.open_delimiters.len() - unclosed_delimiter_show_limit - ), - ); - } - - if let Some((delim, _)) = self.diag_info.open_delimiters.last() { - report_suspicious_mismatch_block( - &mut err, - &self.diag_info, - self.psess.source_map(), - *delim, - ) - } - err - } - fn lex_token_tree_open_delim( &mut self, open_delim: Delimiter, @@ -206,13 +167,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } else if let Some(glued) = self.token.glue(&next_tok) { self.token = glued; } else { - let this_spacing = if next_tok.is_punct() { - Spacing::Joint - } else if next_tok == token::Eof { - Spacing::Alone - } else { - Spacing::JointHidden - }; + let this_spacing = self.calculate_spacing(&next_tok); break (this_spacing, next_tok); } }; @@ -223,23 +178,64 @@ impl<'psess, 'src> Lexer<'psess, 'src> { // Cut-down version of `bump` used when the token kind is known in advance. fn bump_minimal(&mut self) -> Spacing { let (next_tok, is_next_tok_preceded_by_whitespace) = self.next_token_from_cursor(); - let this_spacing = if is_next_tok_preceded_by_whitespace { Spacing::Alone } else { - if next_tok.is_punct() { - Spacing::Joint - } else if next_tok == token::Eof { - Spacing::Alone - } else { - Spacing::JointHidden - } + self.calculate_spacing(&next_tok) }; - self.token = next_tok; this_spacing } + fn calculate_spacing(&self, next_tok: &Token) -> Spacing { + if next_tok.is_punct() { + Spacing::Joint + } else if *next_tok == token::Eof { + Spacing::Alone + } else { + Spacing::JointHidden + } + } + + fn eof_err(&mut self) -> Diag<'psess> { + const UNCLOSED_DELIMITER_SHOW_LIMIT: usize = 5; + let msg = "this file contains an unclosed delimiter"; + let mut err = self.dcx().struct_span_err(self.token.span, msg); + + let len = usize::min(UNCLOSED_DELIMITER_SHOW_LIMIT, self.diag_info.open_delimiters.len()); + for &(_, span) in &self.diag_info.open_delimiters[..len] { + err.span_label(span, "unclosed delimiter"); + self.diag_info.unmatched_delims.push(UnmatchedDelim { + found_delim: None, + found_span: self.token.span, + unclosed_span: Some(span), + candidate_span: None, + }); + } + + if let Some((_, span)) = self.diag_info.open_delimiters.get(UNCLOSED_DELIMITER_SHOW_LIMIT) + && self.diag_info.open_delimiters.len() >= UNCLOSED_DELIMITER_SHOW_LIMIT + 2 + { + err.span_label( + *span, + format!( + "another {} unclosed delimiters begin from here", + self.diag_info.open_delimiters.len() - UNCLOSED_DELIMITER_SHOW_LIMIT + ), + ); + } + + if let Some((delim, _)) = self.diag_info.open_delimiters.last() { + report_suspicious_mismatch_block( + &mut err, + &self.diag_info, + self.psess.source_map(), + *delim, + ) + } + err + } + fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> { // An unexpected closing delimiter (i.e., there is no matching opening delimiter). let token_str = token_to_string(&self.token); -- cgit 1.4.1-3-g733a5 From a460b46d0f7d95a0a07bad444606f6fd53aea62c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 12 Jul 2025 17:42:39 +0200 Subject: Ports `#[macro_use]` and `#[macro_escape]` to the new attribute parsing infrastructure --- .../rustc_attr_data_structures/src/attributes.rs | 19 ++++ .../src/encode_cross_crate.rs | 2 + compiler/rustc_attr_data_structures/src/lib.rs | 4 +- .../src/attributes/macro_attrs.rs | 115 +++++++++++++++++++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + .../rustc_attr_parsing/src/attributes/must_use.rs | 2 +- compiler/rustc_attr_parsing/src/context.rs | 14 +++ .../rustc_attr_parsing/src/session_diagnostics.rs | 10 +- compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_parse/src/validate_attr.rs | 2 + compiler/rustc_passes/src/check_attr.rs | 17 ++- compiler/rustc_resolve/messages.ftl | 2 - compiler/rustc_resolve/src/build_reduced_graph.rs | 67 ++++++------ compiler/rustc_resolve/src/errors.rs | 7 -- src/tools/clippy/clippy_lints/src/macro_use.rs | 8 +- 15 files changed, 206 insertions(+), 65 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9f99b33adcc..1e2576bef2c 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -157,6 +157,19 @@ pub enum UsedBy { Linker, } +#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum MacroUseArgs { + UseAll, + UseSpecific(ThinVec), +} + +impl Default for MacroUseArgs { + fn default() -> Self { + Self::UseSpecific(ThinVec::new()) + } +} + #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] pub struct StrippedCfgItem { pub parent_module: ModId, @@ -351,9 +364,15 @@ pub enum AttributeKind { /// Represents `#[loop_match]`. LoopMatch(Span), + /// Represents `#[macro_escape]`. + MacroEscape(Span), + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), + /// Represents `#[macro_use]`. + MacroUse { span: Span, arguments: MacroUseArgs }, + /// Represents `#[marker]`. Marker(Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 86d9ddba4d2..159b807a3b2 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -45,7 +45,9 @@ impl AttributeKind { LinkOrdinal { .. } => No, LinkSection { .. } => Yes, // Needed for rustdoc LoopMatch(..) => No, + MacroEscape(..) => No, MacroTransparency(..) => Yes, + MacroUse { .. } => No, Marker(..) => No, MayDangle(..) => No, MustUse { .. } => Yes, diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index ecca0e39063..4c5af805ca9 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; use rustc_span::hygiene::Transparency; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; pub use stability::*; use thin_vec::ThinVec; pub use version::*; @@ -172,7 +172,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); print_disp!(u16, bool, NonZero); -print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); +print_debug!(Symbol, Ident, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); /// Finds attributes in sequences of attributes by pattern matching. /// diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs new file mode 100644 index 00000000000..eade49180ac --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -0,0 +1,115 @@ +use rustc_attr_data_structures::{AttributeKind, MacroUseArgs}; +use rustc_errors::DiagArgValue; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; + +use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate}; +use crate::context::{AcceptContext, FinalizeContext, Stage}; +use crate::parser::ArgParser; +use crate::session_diagnostics; + +pub(crate) struct MacroEscapeParser; +impl NoArgsAttributeParser for MacroEscapeParser { + const PATH: &[Symbol] = &[sym::macro_escape]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::MacroEscape; +} + +/// `#[macro_use]` attributes can either: +/// - Use all macros from a crate, if provided without arguments +/// - Use specific macros from a crate, if provided with arguments `#[macro_use(macro1, macro2)]` +/// A warning should be provided if an use all is combined with specific uses, or if multiple use-alls are used. +#[derive(Default)] +pub(crate) struct MacroUseParser { + state: MacroUseArgs, + + /// Spans of all `#[macro_use]` arguments with arguments, used for linting + uses_attr_spans: ThinVec, + /// If `state` is `UseSpecific`, stores the span of the first `#[macro_use]` argument, used as the span for this attribute + /// If `state` is `UseAll`, stores the span of the first `#[macro_use]` arguments without arguments + first_span: Option, +} + +const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ..."); + +impl AttributeParser for MacroUseParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::macro_use], + MACRO_USE_TEMPLATE, + |group: &mut Self, cx: &mut AcceptContext<'_, '_, S>, args| { + let span = cx.attr_span; + group.first_span.get_or_insert(span); + match args { + ArgParser::NoArgs => { + match group.state { + MacroUseArgs::UseAll => { + let first_span = group.first_span.expect( + "State is UseAll is some so this is not the first attribute", + ); + // Since there is a `#[macro_use]` import already, give a warning + cx.warn_unused_duplicate(first_span, span); + } + MacroUseArgs::UseSpecific(_) => { + group.state = MacroUseArgs::UseAll; + group.first_span = Some(span); + // If there is a `#[macro_use]` attribute, warn on all `#[macro_use(...)]` attributes since everything is already imported + for specific_use in group.uses_attr_spans.drain(..) { + cx.warn_unused_duplicate(span, specific_use); + } + } + } + } + ArgParser::List(list) => { + if list.is_empty() { + cx.warn_empty_attribute(list.span); + return; + } + + match &mut group.state { + MacroUseArgs::UseAll => { + let first_span = group.first_span.expect( + "State is UseAll is some so this is not the first attribute", + ); + cx.warn_unused_duplicate(first_span, span); + } + MacroUseArgs::UseSpecific(arguments) => { + // Store here so if we encounter a `UseAll` later we can still lint this attribute + group.uses_attr_spans.push(cx.attr_span); + + for item in list.mixed() { + let Some(item) = item.meta_item() else { + cx.expected_identifier(item.span()); + continue; + }; + if let Err(err_span) = item.args().no_args() { + cx.expected_no_args(err_span); + continue; + } + let Some(item) = item.path().word() else { + cx.expected_identifier(item.span()); + continue; + }; + arguments.push(item); + } + } + } + } + ArgParser::NameValue(_) => { + let suggestions = MACRO_USE_TEMPLATE.suggestions(false, sym::macro_use); + cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + } + } + }, + )]; + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 200f1381960..15b90bd2ed7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -36,6 +36,7 @@ pub(crate) mod inline; pub(crate) mod link_attrs; pub(crate) mod lint_helpers; pub(crate) mod loop_match; +pub(crate) mod macro_attrs; pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index e0a3e675509..42af3ed0bfa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -34,7 +34,7 @@ impl SingleAttributeParser for MustUseParser { ArgParser::List(_) => { let suggestions = >::TEMPLATE.suggestions(false, "must_use"); - cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput { + cx.emit_err(session_diagnostics::IllFormedAttributeInputLint { num_suggestions: suggestions.len(), suggestions: DiagArgValue::StrListSepByAnd( suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4d692d9562c..45bfe345207 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -33,6 +33,7 @@ use crate::attributes::lint_helpers::{ AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; +use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser}; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; @@ -126,6 +127,7 @@ attribute_parsers!( BodyStabilityParser, ConfusablesParser, ConstStabilityParser, + MacroUseParser, NakedParser, StabilityParser, UsedParser, @@ -174,6 +176,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -386,6 +389,17 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } + /// emit an error that a `name` was expected here + pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedIdentifier, + }) + } + /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> ErrorGuaranteed { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9a400e0fe10..1de25ca252b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -438,7 +438,7 @@ pub(crate) struct IllFormedAttributeInput { #[derive(Diagnostic)] #[diag(attr_parsing_ill_formed_attribute_input)] -pub(crate) struct MustUseIllFormedAttributeInput { +pub(crate) struct IllFormedAttributeInputLint { #[primary_span] pub span: Span, pub num_suggestions: usize, @@ -549,6 +549,7 @@ pub(crate) enum AttributeParseErrorReason { /// Should we tell the user to write a list when they didn't? list: bool, }, + ExpectedIdentifier, } pub(crate) struct AttributeParseError { @@ -600,11 +601,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.code(E0538); } AttributeParseErrorReason::UnexpectedLiteral => { - diag.span_label(self.span, format!("didn't expect a literal here")); + diag.span_label(self.span, "didn't expect a literal here"); diag.code(E0565); } AttributeParseErrorReason::ExpectedNoArgs => { - diag.span_label(self.span, format!("didn't expect any arguments here")); + diag.span_label(self.span, "didn't expect any arguments here"); diag.code(E0565); } AttributeParseErrorReason::ExpectedNameValue(None) => { @@ -684,6 +685,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { } } } + AttributeParseErrorReason::ExpectedIdentifier => { + diag.span_label(self.span, "expected a valid identifier here"); + } } let suggestions = self.template.suggestions(false, &name); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e7898648c2b..e83f6a1df72 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute { // FIXME: should not be needed anymore when all attrs are parsed Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span, Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span, + Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => *span, Attribute::Parsed(AttributeKind::MayDangle(span)) => *span, Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span, Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span, diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index cca621103b5..ed7fb774908 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -303,6 +303,8 @@ pub fn check_builtin_meta_item( | sym::cold | sym::target_feature | sym::rustc_allow_const_fn_unstable + | sym::macro_use + | sym::macro_escape | sym::naked | sym::no_mangle | sym::non_exhaustive diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 96c895e71df..04c1933f243 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -227,6 +227,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::LinkSection { span: attr_span, .. }) => { self.check_link_section(hir_id, *attr_span, span, target) } + Attribute::Parsed(AttributeKind::MacroUse { span, .. }) => { + self.check_macro_use(hir_id, sym::macro_use, *span, target) + } + Attribute::Parsed(AttributeKind::MacroEscape(span)) => { + self.check_macro_use(hir_id, sym::macro_escape, *span, target) + } Attribute::Parsed(AttributeKind::Naked(attr_span)) => { self.check_naked(hir_id, *attr_span, span, target) } @@ -362,9 +368,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), [sym::link, ..] => self.check_link(hir_id, attr, span, target), - [sym::macro_use, ..] | [sym::macro_escape, ..] => { - self.check_macro_use(hir_id, attr, target) - } [sym::path, ..] => self.check_generic_attr_unparsed(hir_id, attr, target, Target::Mod), [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::should_panic, ..] => { @@ -2411,17 +2414,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { - let Some(name) = attr.name() else { - return; - }; + fn check_macro_use(&self, hir_id: HirId, name: Symbol, attr_span: Span, target: Target) { match target { Target::ExternCrate | Target::Mod => {} _ => { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::MacroUse { name }, ); } @@ -2474,7 +2474,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // Warn on useless empty attributes. // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute` let note = if attr.has_any_name(&[ - sym::macro_use, sym::allow, sym::expect, sym::warn, diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index aa818cc9c46..39e9a9cc58a 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -41,8 +41,6 @@ resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion = resolve_attributes_starting_with_rustc_are_reserved = attributes starting with `rustc` are reserved for use by the `rustc` compiler -resolve_bad_macro_import = bad macro import - resolve_binding_in_never_pattern = never patterns cannot contain variable bindings .suggestion = use a wildcard `_` instead diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 737577baa7a..fe7fa71a7fc 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -11,11 +11,14 @@ use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; use rustc_ast::{ self as ast, AssocItem, AssocItemKind, Block, ConstItem, Delegation, Fn, ForeignItem, - ForeignItemKind, Impl, Item, ItemKind, MetaItemKind, NodeId, StaticItem, StmtKind, TyAlias, + ForeignItemKind, Impl, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias, }; +use rustc_attr_data_structures::{AttributeKind, MacroUseArgs}; use rustc_attr_parsing as attr; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; +use rustc_hir::Attribute; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_index::bit_set::DenseBitSet; @@ -25,6 +28,7 @@ use rustc_middle::metadata::ModChild; use rustc_middle::ty::{Feed, Visibility}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; use rustc_span::{Ident, Span, Symbol, kw, sym}; +use thin_vec::ThinVec; use tracing::debug; use crate::Namespace::{MacroNS, TypeNS, ValueNS}; @@ -1019,42 +1023,31 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { /// Returns `true` if we should consider the underlying `extern crate` to be used. fn process_macro_use_imports(&mut self, item: &Item, module: Module<'ra>) -> bool { let mut import_all = None; - let mut single_imports = Vec::new(); - for attr in &item.attrs { - if attr.has_name(sym::macro_use) { - if self.parent_scope.module.parent.is_some() { - self.r.dcx().emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { - span: item.span, - }); - } - if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind - && orig_name == kw::SelfLower - { - self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span }); - } - let ill_formed = |span| { - self.r.dcx().emit_err(errors::BadMacroImport { span }); - }; - match attr.meta() { - Some(meta) => match meta.kind { - MetaItemKind::Word => { - import_all = Some(meta.span); - break; - } - MetaItemKind::List(meta_item_inners) => { - for meta_item_inner in meta_item_inners { - match meta_item_inner.ident() { - Some(ident) if meta_item_inner.is_word() => { - single_imports.push(ident) - } - _ => ill_formed(meta_item_inner.span()), - } - } - } - MetaItemKind::NameValue(..) => ill_formed(meta.span), - }, - None => ill_formed(attr.span), - } + let mut single_imports = ThinVec::new(); + if let Some(Attribute::Parsed(AttributeKind::MacroUse { span, arguments })) = + AttributeParser::parse_limited( + self.r.tcx.sess, + &item.attrs, + sym::macro_use, + item.span, + item.id, + None, + ) + { + if self.parent_scope.module.parent.is_some() { + self.r + .dcx() + .emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { span: item.span }); + } + if let ItemKind::ExternCrate(Some(orig_name), _) = item.kind + && orig_name == kw::SelfLower + { + self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span }); + } + + match arguments { + MacroUseArgs::UseAll => import_all = Some(span), + MacroUseArgs::UseSpecific(imports) => single_imports = imports, } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index b34bcb38f84..d6b1e4de6ea 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -815,13 +815,6 @@ pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { pub(crate) span: Span, } -#[derive(Diagnostic)] -#[diag(resolve_bad_macro_import, code = E0466)] -pub(crate) struct BadMacroImport { - #[primary_span] - pub(crate) span: Span, -} - #[derive(Diagnostic)] #[diag(resolve_extern_crate_self_requires_renaming)] pub(crate) struct ExternCrateSelfRequiresRenaming { diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index c1a26c5a9c7..d1a54df988f 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -7,8 +7,9 @@ use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{Span, sym}; +use rustc_span::{Span}; use std::collections::BTreeMap; +use rustc_attr_data_structures::{AttributeKind, find_attr}; declare_clippy_lint! { /// ### What it does @@ -99,15 +100,14 @@ impl LateLintPass<'_> for MacroUseImports { && let hir::ItemKind::Use(path, _kind) = &item.kind && let hir_id = item.hir_id() && let attrs = cx.tcx.hir_attrs(hir_id) - && let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)) + && let Some(mac_attr_span) = find_attr!(attrs, AttributeKind::MacroUse {span, ..} => *span) && let Some(Res::Def(DefKind::Mod, id)) = path.res.type_ns && !id.is_local() { for kid in cx.tcx.module_children(id) { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span(); let def_path = cx.tcx.def_path_str(mac_id); - self.imports.push((def_path, span, hir_id)); + self.imports.push((def_path, mac_attr_span, hir_id)); } } } else if item.span.from_expansion() { -- cgit 1.4.1-3-g733a5