about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs14
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs83
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs2
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl2
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs44
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs7
-rw-r--r--compiler/rustc_passes/messages.ftl3
-rw-r--r--compiler/rustc_passes/src/check_attr.rs50
-rw-r--r--compiler/rustc_passes/src/errors.rs7
-rw-r--r--tests/ui/attributes/used_with_arg.rs15
-rw-r--r--tests/ui/attributes/used_with_arg.stderr39
-rw-r--r--tests/ui/attributes/used_with_multi_args.rs2
-rw-r--r--tests/ui/attributes/used_with_multi_args.stderr16
-rw-r--r--tests/ui/lint/unused/unused-attr-duplicate.stderr24
15 files changed, 185 insertions, 124 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index d755afad59f..60a4f289306 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -131,6 +131,17 @@ impl Deprecation {
     }
 }
 
+/// There are three valid forms of the attribute:
+/// `#[used]`, which is semantically equivalent to `#[used(linker)]` except that the latter is currently unstable.
+/// `#[used(compiler)]`
+/// `#[used(linker)]`
+#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(HashStable_Generic, PrintAttribute)]
+pub enum UsedBy {
+    Compiler,
+    Linker,
+}
+
 /// Represents parsed *built-in* inert attributes.
 ///
 /// ## Overview
@@ -285,5 +296,8 @@ pub enum AttributeKind {
 
     /// Represents `#[track_caller]`
     TrackCaller(Span),
+
+    /// Represents `#[used]`
+    Used { used_by: UsedBy, span: Span },
     // tidy-alphabetical-end
 }
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 d4402f4e2e6..64bcf1fe6cc 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -38,6 +38,7 @@ impl AttributeKind {
             PubTransparent(..) => Yes,
             SkipDuringMethodDispatch { .. } => No,
             TrackCaller(..) => Yes,
+            Used { .. } => No,
         }
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 5a849e79cc3..7c412d4fa89 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -1,4 +1,4 @@
-use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
+use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
 use rustc_feature::{AttributeTemplate, template};
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
@@ -228,3 +228,84 @@ impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
         Some(AttributeKind::NoMangle(cx.attr_span))
     }
 }
+
+#[derive(Default)]
+pub(crate) struct UsedParser {
+    first_compiler: Option<Span>,
+    first_linker: Option<Span>,
+}
+
+// A custom `AttributeParser` is used rather than a Simple attribute parser because
+// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
+// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
+// We can change this to a Simple parser once the warning becomes an error
+impl<S: Stage> AttributeParser<S> for UsedParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
+        &[sym::used],
+        template!(Word, List: "compiler|linker"),
+        |group: &mut Self, cx, args| {
+            let used_by = match args {
+                ArgParser::NoArgs => UsedBy::Linker,
+                ArgParser::List(list) => {
+                    let Some(l) = list.single() else {
+                        cx.expected_single_argument(list.span);
+                        return;
+                    };
+
+                    match l.meta_item().and_then(|i| i.path().word_sym()) {
+                        Some(sym::compiler) => {
+                            if !cx.features().used_with_arg() {
+                                feature_err(
+                                    &cx.sess(),
+                                    sym::used_with_arg,
+                                    cx.attr_span,
+                                    "`#[used(compiler)]` is currently unstable",
+                                )
+                                .emit();
+                            }
+                            UsedBy::Compiler
+                        }
+                        Some(sym::linker) => {
+                            if !cx.features().used_with_arg() {
+                                feature_err(
+                                    &cx.sess(),
+                                    sym::used_with_arg,
+                                    cx.attr_span,
+                                    "`#[used(linker)]` is currently unstable",
+                                )
+                                .emit();
+                            }
+                            UsedBy::Linker
+                        }
+                        _ => {
+                            cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
+                            return;
+                        }
+                    }
+                }
+                ArgParser::NameValue(_) => return,
+            };
+
+            let target = match used_by {
+                UsedBy::Compiler => &mut group.first_compiler,
+                UsedBy::Linker => &mut group.first_linker,
+            };
+
+            let attr_span = cx.attr_span;
+            if let Some(prev) = *target {
+                cx.warn_unused_duplicate(prev, attr_span);
+            } else {
+                *target = Some(attr_span);
+            }
+        },
+    )];
+
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+        // Ratcheting behaviour, if both `linker` and `compiler` are specified, use `linker`
+        Some(match (self.first_compiler, self.first_linker) {
+            (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
+            (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
+            (None, None) => return None,
+        })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 83e3c75dedb..71bb86ca3d3 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -17,6 +17,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
 use crate::attributes::codegen_attrs::{
     ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TrackCallerParser,
+    UsedParser,
 };
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
@@ -103,6 +104,7 @@ attribute_parsers!(
         ConstStabilityParser,
         NakedParser,
         StabilityParser,
+        UsedParser,
         // tidy-alphabetical-end
 
         // tidy-alphabetical-start
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 6362d2edf85..84d63819343 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -48,8 +48,6 @@ codegen_ssa_error_writing_def_file =
 
 codegen_ssa_expected_name_value_pair = expected name value pair
 
-codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
-
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
 
 codegen_ssa_extract_bundled_libs_archive_member = failed to get data from archive member '{$rlib}': {$error}
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 94df5e8b361..716b2d76c62 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -4,7 +4,7 @@ use rustc_abi::ExternAbi;
 use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
 use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
 use rustc_attr_data_structures::{
-    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, find_attr,
+    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, UsedBy, find_attr,
 };
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -160,6 +160,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     }
                     codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
                 }
+                AttributeKind::Used { used_by, .. } => match used_by {
+                    UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER,
+                    UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER,
+                },
                 _ => {}
             }
         }
@@ -181,44 +185,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             sym::rustc_std_internal_symbol => {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
             }
-            sym::used => {
-                let inner = attr.meta_item_list();
-                match inner.as_deref() {
-                    Some([item]) if item.has_name(sym::linker) => {
-                        if !tcx.features().used_with_arg() {
-                            feature_err(
-                                &tcx.sess,
-                                sym::used_with_arg,
-                                attr.span(),
-                                "`#[used(linker)]` is currently unstable",
-                            )
-                            .emit();
-                        }
-                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
-                    }
-                    Some([item]) if item.has_name(sym::compiler) => {
-                        if !tcx.features().used_with_arg() {
-                            feature_err(
-                                &tcx.sess,
-                                sym::used_with_arg,
-                                attr.span(),
-                                "`#[used(compiler)]` is currently unstable",
-                            )
-                            .emit();
-                        }
-                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER;
-                    }
-                    Some(_) => {
-                        tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() });
-                    }
-                    None => {
-                        // Unconditionally using `llvm.used` causes issues in handling
-                        // `.init_array` with the gold linker. Luckily gold has been
-                        // deprecated with GCC 15 and rustc now warns about using gold.
-                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER
-                    }
-                }
-            }
             sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
             sym::target_feature => {
                 let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index db536af0162..1950a35b364 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -727,13 +727,6 @@ pub struct UnknownArchiveKind<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_used_symbol)]
-pub(crate) struct ExpectedUsedSymbol {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_multiple_main_functions)]
 #[help]
 pub(crate) struct MultipleMainFunctions {
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index e2995daadfe..b1c7b0fcd6c 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -816,9 +816,6 @@ passes_unused_variable_try_prefix = unused variable: `{$name}`
     .suggestion = if this is intentional, prefix it with an underscore
 
 
-passes_used_compiler_linker =
-    `used(compiler)` and `used(linker)` can't be used together
-
 passes_used_static =
     attribute must be applied to a `static` variable
     .label = but this is a {$target}
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 491b3699f4c..877bb9be289 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -200,6 +200,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::NoMangle(attr_span)) => {
                     self.check_no_mangle(hir_id, *attr_span, span, target)
                 }
+                Attribute::Parsed(AttributeKind::Used { span: attr_span, .. }) => {
+                    self.check_used(*attr_span, target, span);
+                }
                 Attribute::Unparsed(attr_item) => {
                     style = Some(attr_item.style);
                     match attr.path().as_slice() {
@@ -333,7 +336,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             | sym::cfi_encoding // FIXME(cfi_encoding)
                             | sym::pointee // FIXME(derive_coerce_pointee)
                             | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section)
-                            | sym::used // handled elsewhere to restrict to static items
                             | sym::instruction_set // broken on stable!!!
                             | sym::windows_subsystem // broken on stable!!!
                             | sym::patchable_function_entry // FIXME(patchable_function_entry)
@@ -403,7 +405,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
 
         self.check_repr(attrs, span, target, item, hir_id);
-        self.check_used(attrs, target, span);
         self.check_rustc_force_inline(hir_id, attrs, span, target);
         self.check_mix_no_mangle_export(hir_id, attrs);
     }
@@ -2107,44 +2108,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_used(&self, attrs: &[Attribute], target: Target, target_span: Span) {
-        let mut used_linker_span = None;
-        let mut used_compiler_span = None;
-        for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
-            if target != Target::Static {
-                self.dcx().emit_err(errors::UsedStatic {
-                    attr_span: attr.span(),
-                    span: target_span,
-                    target: target.name(),
-                });
-            }
-            let inner = attr.meta_item_list();
-            match inner.as_deref() {
-                Some([item]) if item.has_name(sym::linker) => {
-                    if used_linker_span.is_none() {
-                        used_linker_span = Some(attr.span());
-                    }
-                }
-                Some([item]) if item.has_name(sym::compiler) => {
-                    if used_compiler_span.is_none() {
-                        used_compiler_span = Some(attr.span());
-                    }
-                }
-                Some(_) => {
-                    // This error case is handled in rustc_hir_analysis::collect.
-                }
-                None => {
-                    // Default case (compiler) when arg isn't defined.
-                    if used_compiler_span.is_none() {
-                        used_compiler_span = Some(attr.span());
-                    }
-                }
-            }
-        }
-        if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
-            self.tcx
-                .dcx()
-                .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] });
+    fn check_used(&self, attr_span: Span, target: Target, target_span: Span) {
+        if target != Target::Static {
+            self.dcx().emit_err(errors::UsedStatic {
+                attr_span,
+                span: target_span,
+                target: target.name(),
+            });
         }
     }
 
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 3286ccc94f2..f89d925202c 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -642,13 +642,6 @@ pub(crate) struct UsedStatic {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_used_compiler_linker)]
-pub(crate) struct UsedCompilerLinker {
-    #[primary_span]
-    pub spans: Vec<Span>,
-}
-
-#[derive(Diagnostic)]
 #[diag(passes_allow_internal_unstable)]
 pub(crate) struct AllowInternalUnstable {
     #[primary_span]
diff --git a/tests/ui/attributes/used_with_arg.rs b/tests/ui/attributes/used_with_arg.rs
index ad80ff53f0e..bc7a6f07442 100644
--- a/tests/ui/attributes/used_with_arg.rs
+++ b/tests/ui/attributes/used_with_arg.rs
@@ -1,3 +1,4 @@
+#![deny(unused_attributes)]
 #![feature(used_with_arg)]
 
 #[used(linker)]
@@ -6,14 +7,22 @@ static mut USED_LINKER: [usize; 1] = [0];
 #[used(compiler)]
 static mut USED_COMPILER: [usize; 1] = [0];
 
-#[used(compiler)] //~ ERROR `used(compiler)` and `used(linker)` can't be used together
+#[used(compiler)]
 #[used(linker)]
 static mut USED_COMPILER_LINKER2: [usize; 1] = [0];
 
-#[used(compiler)] //~ ERROR `used(compiler)` and `used(linker)` can't be used together
-#[used(linker)]
 #[used(compiler)]
 #[used(linker)]
+#[used(compiler)] //~ ERROR unused attribute
+#[used(linker)] //~ ERROR unused attribute
 static mut USED_COMPILER_LINKER3: [usize; 1] = [0];
 
+#[used(compiler)]
+#[used]
+static mut USED_WITHOUT_ATTR1: [usize; 1] = [0];
+
+#[used(linker)]
+#[used] //~ ERROR unused attribute
+static mut USED_WITHOUT_ATTR2: [usize; 1] = [0];
+
 fn main() {}
diff --git a/tests/ui/attributes/used_with_arg.stderr b/tests/ui/attributes/used_with_arg.stderr
index 440e5c4a5a0..9ff91a4e03b 100644
--- a/tests/ui/attributes/used_with_arg.stderr
+++ b/tests/ui/attributes/used_with_arg.stderr
@@ -1,18 +1,43 @@
-error: `used(compiler)` and `used(linker)` can't be used together
-  --> $DIR/used_with_arg.rs:9:1
+error: unused attribute
+  --> $DIR/used_with_arg.rs:16:1
+   |
+LL | #[used(compiler)]
+   | ^^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/used_with_arg.rs:14:1
    |
 LL | #[used(compiler)]
    | ^^^^^^^^^^^^^^^^^
+note: the lint level is defined here
+  --> $DIR/used_with_arg.rs:1:9
+   |
+LL | #![deny(unused_attributes)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: unused attribute
+  --> $DIR/used_with_arg.rs:17:1
+   |
+LL | #[used(linker)]
+   | ^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/used_with_arg.rs:15:1
+   |
 LL | #[used(linker)]
    | ^^^^^^^^^^^^^^^
 
-error: `used(compiler)` and `used(linker)` can't be used together
-  --> $DIR/used_with_arg.rs:13:1
+error: unused attribute
+  --> $DIR/used_with_arg.rs:25:1
+   |
+LL | #[used]
+   | ^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/used_with_arg.rs:24:1
    |
-LL | #[used(compiler)]
-   | ^^^^^^^^^^^^^^^^^
 LL | #[used(linker)]
    | ^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/attributes/used_with_multi_args.rs b/tests/ui/attributes/used_with_multi_args.rs
index d3109cc6444..1c054f792eb 100644
--- a/tests/ui/attributes/used_with_multi_args.rs
+++ b/tests/ui/attributes/used_with_multi_args.rs
@@ -1,6 +1,6 @@
 #![feature(used_with_arg)]
 
-#[used(compiler, linker)] //~ ERROR expected `used`, `used(compiler)` or `used(linker)`
+#[used(compiler, linker)] //~ ERROR malformed `used` attribute input
 static mut USED_COMPILER_LINKER: [usize; 1] = [0];
 
 fn main() {}
diff --git a/tests/ui/attributes/used_with_multi_args.stderr b/tests/ui/attributes/used_with_multi_args.stderr
index d4417a202d5..e48209cf204 100644
--- a/tests/ui/attributes/used_with_multi_args.stderr
+++ b/tests/ui/attributes/used_with_multi_args.stderr
@@ -1,8 +1,20 @@
-error: expected `used`, `used(compiler)` or `used(linker)`
+error[E0805]: malformed `used` attribute input
   --> $DIR/used_with_multi_args.rs:3:1
    |
 LL | #[used(compiler, linker)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^------------------^
+   |       |
+   |       expected a single argument here
+   |
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[used(compiler, linker)]
+LL + #[used(compiler|linker)]
+   |
+LL - #[used(compiler, linker)]
+LL + #[used]
+   |
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0805`.
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index 3a520ffcb3d..a18581192ea 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -90,18 +90,6 @@ LL | #[automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:102:1
-   |
-LL | #[used]
-   | ^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:101:1
-   |
-LL | #[used]
-   | ^^^^^^^
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:86:5
    |
 LL |     #[link_name = "this_does_not_exist"]
@@ -289,5 +277,17 @@ note: attribute also specified here
 LL | #[no_mangle]
    | ^^^^^^^^^^^^
 
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:102:1
+   |
+LL | #[used]
+   | ^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:101:1
+   |
+LL | #[used]
+   | ^^^^^^^
+
 error: aborting due to 23 previous errors