about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src
diff options
context:
space:
mode:
authorJana Dönszelmann <jana@donsz.nl>2025-06-10 13:00:09 +0200
committerJana Dönszelmann <jana@donsz.nl>2025-06-12 09:42:45 +0200
commit28bf61b9b32a2db0a9a7f32d75349b4e0db72201 (patch)
tree500de2761dc6e0ce93d1bf56abb3a28bc288a5b3 /compiler/rustc_attr_parsing/src
parentfe5c95d4ae33ec9d7831921e448e2daf8264ea42 (diff)
downloadrust-28bf61b9b32a2db0a9a7f32d75349b4e0db72201.tar.gz
rust-28bf61b9b32a2db0a9a7f32d75349b4e0db72201.zip
introduce duplicate attribute diagnostic logic
Diffstat (limited to 'compiler/rustc_attr_parsing/src')
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/deprecation.rs9
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs106
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs9
4 files changed, 112 insertions, 17 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index 006c1fe3b9c..dcb12ac29cd 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -1,8 +1,8 @@
 use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
 use rustc_span::{Span, Symbol, sym};
 
-use super::SingleAttributeParser;
 use super::util::parse_version;
+use super::{AttributeDuplicates, SingleAttributeParser};
 use crate::context::AcceptContext;
 use crate::parser::ArgParser;
 use crate::session_diagnostics;
@@ -43,12 +43,13 @@ fn get(
 
 impl SingleAttributeParser for DeprecationParser {
     const PATH: &'static [Symbol] = &[sym::deprecated];
+    const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing;
 
-    fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span) {
+    fn on_duplicate(cx: &AcceptContext<'_>, used: Span, unused: Span) {
         // FIXME(jdonszelmann): merge with errors from check_attrs.rs
         cx.emit_err(session_diagnostics::UnusedMultiple {
-            this: cx.attr_span,
-            other: first_span,
+            this: used,
+            other: unused,
             name: sym::deprecated,
         });
     }
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index 7cceca3c24d..7aac4744504 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -22,6 +22,7 @@ use thin_vec::ThinVec;
 
 use crate::context::{AcceptContext, FinalizeContext};
 use crate::parser::ArgParser;
+use crate::session_diagnostics::UnusedMultiple;
 
 pub(crate) mod allow_unstable;
 pub(crate) mod cfg;
@@ -83,11 +84,28 @@ pub(crate) trait AttributeParser: Default + 'static {
 pub(crate) trait SingleAttributeParser: 'static {
     const PATH: &'static [Symbol];
 
+<<<<<<< Conflict 1 of 1
++++++++ Contents of side #1
     /// Called when a duplicate attribute is found.
+%%%%%%% Changes from base to side #2
++    const ON_DUPLICATE_STRATEGY: AttributeDuplicates;
++
+     /// Caled when a duplicate attribute is found.
+>>>>>>> Conflict 1 of 1 ends
     ///
-    /// `first_span` is the span of the first occurrence of this attribute.
+    /// - `unused` is the span of the attribute that was unused or bad because of some
+    ///   duplicate reason (see [`AttributeDuplicates`])
+    /// - `used` is the span of the attribute that was used in favor of the unused attribute
     // FIXME(jdonszelmann): default error
-    fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
+    fn on_duplicate(cx: &AcceptContext<'_>, used: Span, unused: Span) {
+        cx.emit_err(UnusedMultiple {
+            this: used,
+            other: unused,
+            name: Symbol::intern(
+                &Self::PATH.into_iter().map(|i| i.to_string()).collect::<Vec<_>>().join(".."),
+            ),
+        });
+    }
 
     /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
     fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
@@ -103,12 +121,24 @@ impl<T: SingleAttributeParser> Default for Single<T> {
 
 impl<T: SingleAttributeParser> AttributeParser for Single<T> {
     const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
-        if let Some((_, s)) = group.1 {
-            T::on_duplicate(cx, s);
-            return;
-        }
-
         if let Some(pa) = T::convert(cx, args) {
+            match T::ON_DUPLICATE_STRATEGY {
+                // keep the first and error
+                AttributeDuplicates::ErrorFollowing => {
+                    if let Some((_, unused)) = group.1 {
+                        T::on_duplicate(cx, cx.attr_span, unused);
+                        return;
+                    }
+                }
+                // keep the new one and warn about the previous,
+                // then replace
+                AttributeDuplicates::FutureWarnPreceding => {
+                    if let Some((_, used)) = group.1 {
+                        T::on_duplicate(cx, used, cx.attr_span);
+                    }
+                }
+            }
+
             group.1 = Some((pa, cx.attr_span));
         }
     })];
@@ -118,6 +148,68 @@ impl<T: SingleAttributeParser> AttributeParser for Single<T> {
     }
 }
 
+pub(crate) enum OnDuplicate {
+    /// Give a default warning
+    Warn,
+
+    /// Duplicates will be a warning, with a note that this will be an error in the future.
+    WarnButFutureError,
+
+    /// Give a default error
+    Error,
+
+    /// Ignore duplicates
+    Ignore,
+
+    /// Custom function called when a duplicate attribute is found.
+    ///
+    /// - `unused` is the span of the attribute that was unused or bad because of some
+    ///   duplicate reason (see [`AttributeDuplicates`])
+    /// - `used` is the span of the attribute that was used in favor of the unused attribute
+    Custom(fn(cx: &AcceptContext<'_>, used: Span, unused: Span)),
+}
+
+impl OnDuplicate {
+    fn exec<P: SingleAttributeParser>(&self, cx: &AcceptContext<'_>, used: Span, unused: Span) {
+        match self {
+            OnDuplicate::Warn => {
+                todo!()
+            }
+            OnDuplicate::WarnButFutureError => {
+                todo!()
+            }
+            OnDuplicate::Error => {
+                cx.emit_err(UnusedMultiple {
+                    this: used,
+                    other: unused,
+                    name: Symbol::intern(
+                        &P::PATH.into_iter().map(|i| i.to_string()).collect::<Vec<_>>().join(".."),
+                    ),
+                });
+            }
+            OnDuplicate::Ignore => {}
+            OnDuplicate::Custom(f) => f(cx, used, unused),
+        }
+    }
+}
+
+pub(crate) enum AttributeDuplicates {
+    /// Duplicates after the first attribute will be an error.
+    ///
+    /// This should be used where duplicates would be ignored, but carry extra
+    /// meaning that could cause confusion. For example, `#[stable(since="1.0")]
+    /// #[stable(since="2.0")]`, which version should be used for `stable`?
+    ErrorFollowing,
+
+    /// Duplicates preceding the last instance of the attribute will be a
+    /// warning, with a note that this will be an error in the future.
+    ///
+    /// This is the same as `FutureWarnFollowing`, except the last attribute is
+    /// the one that is "used". Ideally these can eventually migrate to
+    /// `ErrorPreceding`.
+    FutureWarnPreceding,
+}
+
 type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
 
 /// Alternative to [`AttributeParser`] that automatically handles state management.
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index ce69a54513d..407fd5e1b8b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -8,7 +8,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_span::{Span, Symbol, sym};
 
 use super::util::parse_version;
-use super::{AcceptMapping, AttributeParser, SingleAttributeParser};
+use super::{AcceptMapping, AttributeDuplicates, AttributeParser, SingleAttributeParser};
 use crate::context::{AcceptContext, FinalizeContext};
 use crate::parser::{ArgParser, MetaItemParser};
 use crate::session_diagnostics::{self, UnsupportedLiteralReason};
@@ -118,9 +118,10 @@ pub(crate) struct ConstStabilityIndirectParser;
 // FIXME(jdonszelmann): single word attribute group when we have these
 impl SingleAttributeParser for ConstStabilityIndirectParser {
     const PATH: &'static [Symbol] = &[sym::rustc_const_stable_indirect];
+    const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing;
 
     // ignore
-    fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
+    fn on_duplicate(_cx: &AcceptContext<'_>, _used: Span, _unused: Span) {}
 
     fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
         Some(AttributeKind::ConstStabilityIndirect)
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index d229fc09740..0ab4cb99a62 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -2,7 +2,7 @@ use rustc_attr_data_structures::AttributeKind;
 use rustc_span::hygiene::Transparency;
 use rustc_span::{Span, Symbol, sym};
 
-use super::{AcceptContext, SingleAttributeParser};
+use super::{AcceptContext, AttributeDuplicates, SingleAttributeParser};
 use crate::parser::ArgParser;
 
 pub(crate) struct TransparencyParser;
@@ -11,10 +11,11 @@ pub(crate) struct TransparencyParser;
 #[allow(rustc::untranslatable_diagnostic)]
 #[allow(rustc::diagnostic_outside_of_impl)]
 impl SingleAttributeParser for TransparencyParser {
-    const PATH: &'static [Symbol] = &[sym::rustc_macro_transparency];
+    const PATH: &[Symbol] = &[sym::rustc_macro_transparency];
+    const ON_DUPLICATE_STRATEGY: AttributeDuplicates = AttributeDuplicates::ErrorFollowing;
 
-    fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: Span) {
-        cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
+    fn on_duplicate(cx: &crate::context::AcceptContext<'_>, used: Span, unused: Span) {
+        cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
     }
 
     fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {