about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_attr_parsing/src/context.rs')
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs96
1 files changed, 76 insertions, 20 deletions
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 3e1ef688733..ca95c67b418 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -10,7 +10,7 @@ use rustc_ast::NodeId;
 use rustc_attr_data_structures::AttributeKind;
 use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
 use rustc_errors::{DiagCtxtHandle, Diagnostic};
-use rustc_feature::Features;
+use rustc_feature::{AttributeTemplate, Features};
 use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
 use rustc_session::Session;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
@@ -27,11 +27,12 @@ use crate::attributes::stability::{
 use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single};
 use crate::parser::{ArgParser, MetaItemParser};
+use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason};
 
 macro_rules! group_type {
     ($stage: ty) => {
          LazyLock<(
-            BTreeMap<&'static [Symbol], Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>>,
+            BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>,
             Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
         )>
     };
@@ -60,7 +61,7 @@ macro_rules! attribute_parsers {
         @[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
     ) => {
         pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
-            let mut accepts = BTreeMap::<_, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>>::new();
+            let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new();
             let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
             $(
                 {
@@ -68,13 +69,12 @@ macro_rules! attribute_parsers {
                         static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
                     };
 
-                    for (k, v) in <$names>::ATTRIBUTES {
-                        let old = accepts.insert(*k, Box::new(|cx, args| {
+                    for (path, template, accept_fn) in <$names>::ATTRIBUTES {
+                        accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| {
                             STATE_OBJECT.with_borrow_mut(|s| {
-                                v(s, cx, args)
+                                accept_fn(s, cx, args)
                             })
-                        }));
-                        assert!(old.is_none());
+                        })));
                     }
 
                     finalizes.push(Box::new(|cx| {
@@ -168,6 +168,14 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
     pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
     /// The span of the attribute currently being parsed
     pub(crate) attr_span: Span,
+
+    /// The expected structure of the attribute.
+    ///
+    /// Used in reporting errors to give a hint to users what the attribute *should* look like.
+    pub(crate) template: &'f AttributeTemplate,
+
+    /// The name of the attribute we're currently accepting.
+    pub(crate) attr_path: AttrPath,
 }
 
 impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
@@ -175,10 +183,54 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         S::emit_err(&self.sess, diag)
     }
 
+    /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
+    /// must be delayed until after HIR is built. This method will take care of the details of
+    /// that.
     pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
         let id = self.target_id;
         (self.emit_lint)(AttributeLint { id, span, kind: lint });
     }
+
+    pub(crate) fn expected_string_literal(&self, span: Span) -> ErrorGuaranteed {
+        // 539?
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedStringLiteral,
+        })
+    }
+
+    // pub(crate) fn expected_any_arguments(&self, span: Span) -> ErrorGuaranteed {
+    //
+    // }
+
+    pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
+        // E534?
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedSingleArgument,
+        })
+    }
+
+    pub(crate) fn expected_specific_argument(
+        &self,
+        span: Span,
+        options: Vec<&'static str>,
+    ) -> ErrorGuaranteed {
+        // E535?
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedSpecificArgument(options),
+        })
+    }
 }
 
 impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
@@ -377,18 +429,22 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
                     let args = parser.args();
                     let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
 
-                    if let Some(accept) = S::parsers().0.get(parts.as_slice()) {
-                        let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
-                            finalize_cx: FinalizeContext {
-                                cx: self,
-                                target_span,
-                                target_id,
-                                emit_lint: &mut emit_lint,
-                            },
-                            attr_span: lower_span(attr.span),
-                        };
-
-                        accept(&mut cx, args)
+                    if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
+                        for (template, accept) in accepts {
+                            let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
+                                finalize_cx: FinalizeContext {
+                                    cx: self,
+                                    target_span,
+                                    target_id,
+                                    emit_lint: &mut emit_lint,
+                                },
+                                attr_span: lower_span(attr.span),
+                                template,
+                                attr_path: path.get_attribute_path(),
+                            };
+
+                            accept(&mut cx, args)
+                        }
                     } else {
                         // If we're here, we must be compiling a tool attribute... Or someone
                         // forgot to parse their fancy new attribute. Let's warn them in any case.