diff options
Diffstat (limited to 'compiler/rustc_attr_parsing/src')
5 files changed, 138 insertions, 4 deletions
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<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser { + const PATH: &[Symbol] = &[sym::macro_escape]; + const ON_DUPLICATE: OnDuplicate<S> = 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<Span>, + /// 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<Span>, +} + +const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ..."); + +impl<S: Stage> AttributeParser<S> for MacroUseParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[( + &[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<AttributeKind> { + 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<S: Stage> SingleAttributeParser<S> for MustUseParser { ArgParser::List(_) => { let suggestions = <Self as SingleAttributeParser<S>>::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<WithoutArgs<FfiPureParser>>, Single<WithoutArgs<FundamentalParser>>, Single<WithoutArgs<LoopMatchParser>>, + Single<WithoutArgs<MacroEscapeParser>>, Single<WithoutArgs<MarkerParser>>, Single<WithoutArgs<MayDangleParser>>, Single<WithoutArgs<NoImplicitPreludeParser>>, @@ -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<Symbol>) -> 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); |
