about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJana Dönszelmann <jana@donsz.nl>2025-08-11 11:46:30 +0200
committerJana Dönszelmann <jana@donsz.nl>2025-08-24 09:14:49 +0200
commit4b35cde904c1015df42b2c63244c1db1ed51fff9 (patch)
tree244f6f84dd796164a0d9e1be2ce8775f6f498352
parent3bf61444616fc0b9de1e09031f40be0943823fc5 (diff)
downloadrust-4b35cde904c1015df42b2c63244c1db1ed51fff9.tar.gz
rust-4b35cde904c1015df42b2c63244c1db1ed51fff9.zip
Support lints in early attribute parsing
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_attr_parsing/src/interface.rs32
-rw-r--r--compiler/rustc_attr_parsing/src/lints.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs6
-rw-r--r--compiler/rustc_errors/Cargo.toml1
-rw-r--r--compiler/rustc_errors/src/lib.rs6
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_session/src/session.rs17
-rw-r--r--tests/ui/lint/unused/unused_attributes-must_use.fixed2
-rw-r--r--tests/ui/lint/unused/unused_attributes-must_use.rs2
-rw-r--r--tests/ui/lint/unused/unused_attributes-must_use.stderr63
11 files changed, 93 insertions, 45 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 89392631127..af206e87c45 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3791,7 +3791,6 @@ dependencies = [
  "rustc_error_messages",
  "rustc_fluent_macro",
  "rustc_hashes",
- "rustc_hir_id",
  "rustc_index",
  "rustc_lexer",
  "rustc_lint_defs",
diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs
index b4222e41b71..60523c2877c 100644
--- a/compiler/rustc_attr_parsing/src/interface.rs
+++ b/compiler/rustc_attr_parsing/src/interface.rs
@@ -51,6 +51,27 @@ impl<'sess> AttributeParser<'sess, Early> {
         target_node_id: NodeId,
         features: Option<&'sess Features>,
     ) -> Option<Attribute> {
+        Self::parse_limited_should_emit(
+            sess,
+            attrs,
+            sym,
+            target_span,
+            target_node_id,
+            features,
+            ShouldEmit::Nothing,
+        )
+    }
+
+    /// Usually you want `parse_limited`, which defaults to no errors.
+    pub fn parse_limited_should_emit(
+        sess: &'sess Session,
+        attrs: &[ast::Attribute],
+        sym: Symbol,
+        target_span: Span,
+        target_node_id: NodeId,
+        features: Option<&'sess Features>,
+        should_emit: ShouldEmit,
+    ) -> Option<Attribute> {
         let mut parsed = Self::parse_limited_all(
             sess,
             attrs,
@@ -59,7 +80,7 @@ impl<'sess> AttributeParser<'sess, Early> {
             target_span,
             target_node_id,
             features,
-            ShouldEmit::Nothing,
+            should_emit,
         );
         assert!(parsed.len() <= 1);
         parsed.pop()
@@ -84,9 +105,8 @@ impl<'sess> AttributeParser<'sess, Early> {
             target,
             OmitDoc::Skip,
             std::convert::identity,
-            |_lint| {
-                // FIXME: Can't emit lints here for now
-                // This branch can be hit when an attribute produces a warning during early parsing (such as attributes on macro calls)
+            |lint| {
+                crate::lints::emit_attribute_lint(&lint, sess);
             },
         )
     }
@@ -121,8 +141,8 @@ impl<'sess> AttributeParser<'sess, Early> {
                 cx: &mut parser,
                 target_span,
                 target_id: target_node_id,
-                emit_lint: &mut |_lint| {
-                    panic!("can't emit lints here for now (nothing uses this atm)");
+                emit_lint: &mut |lint| {
+                    crate::lints::emit_attribute_lint(&lint, sess);
                 },
             },
             attr_span: attr.span,
diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs
index 7030f28f23c..069478e7f0c 100644
--- a/compiler/rustc_attr_parsing/src/lints.rs
+++ b/compiler/rustc_attr_parsing/src/lints.rs
@@ -1,13 +1,13 @@
 use std::borrow::Cow;
 
 use rustc_errors::{DiagArgValue, LintEmitter};
+use rustc_hir::Target;
 use rustc_hir::lints::{AttributeLint, AttributeLintKind};
-use rustc_hir::{HirId, Target};
 use rustc_span::sym;
 
 use crate::session_diagnostics;
 
-pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emitter: L) {
+pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emitter: L) {
     let AttributeLint { id, span, kind } = lint;
 
     match kind {
@@ -51,7 +51,7 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
                 *id,
                 *span,
                 session_diagnostics::InvalidTargetLint {
-                    name,
+                    name: name.clone(),
                     target: target.plural_name(),
                     applied: DiagArgValue::StrListSepByAnd(
                         applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 2192e8f8f83..72bee0ddfbf 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -484,9 +484,9 @@ pub(crate) struct EmptyAttributeList {
 #[diag(attr_parsing_invalid_target_lint)]
 #[warning]
 #[help]
-pub(crate) struct InvalidTargetLint<'a> {
-    pub name: &'a AttrPath,
-    pub target: &'a str,
+pub(crate) struct InvalidTargetLint {
+    pub name: AttrPath,
+    pub target: &'static str,
     pub applied: DiagArgValue,
     pub only: &'static str,
     #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index f37b6fb748f..7912b8e6098 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -14,7 +14,6 @@ rustc_error_codes = { path = "../rustc_error_codes" }
 rustc_error_messages = { path = "../rustc_error_messages" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_hashes = { path = "../rustc_hashes" }
-rustc_hir_id = { path = "../rustc_hir_id" }
 rustc_index = { path = "../rustc_index" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 38c5716348f..71fc54f0d33 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -61,7 +61,6 @@ pub use rustc_error_messages::{
     fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display,
 };
 use rustc_hashes::Hash128;
-use rustc_hir_id::HirId;
 pub use rustc_lint_defs::{Applicability, listify, pluralize};
 use rustc_lint_defs::{Lint, LintExpectationId};
 use rustc_macros::{Decodable, Encodable};
@@ -110,13 +109,14 @@ rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
 /// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`.
 /// Always the `TyCtxt`.
 pub trait LintEmitter: Copy {
+    type Id: Copy;
     #[track_caller]
     fn emit_node_span_lint(
         self,
         lint: &'static Lint,
-        hir_id: HirId,
+        hir_id: Self::Id,
         span: impl Into<MultiSpan>,
-        decorator: impl for<'a> LintDiagnostic<'a, ()>,
+        decorator: impl for<'a> LintDiagnostic<'a, ()> + DynSend + 'static,
     );
 }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index c215e44a965..4990a85466e 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1421,6 +1421,8 @@ pub struct TyCtxt<'tcx> {
 }
 
 impl<'tcx> LintEmitter for TyCtxt<'tcx> {
+    type Id = HirId;
+
     fn emit_node_span_lint(
         self,
         lint: &'static Lint,
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index bb7ffa2a85d..96767d05439 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -7,6 +7,7 @@ use std::sync::atomic::AtomicBool;
 use std::{env, fmt, io};
 
 use rand::{RngCore, rng};
+use rustc_ast::NodeId;
 use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
@@ -22,7 +23,7 @@ use rustc_errors::timings::TimingSectionHandler;
 use rustc_errors::translation::Translator;
 use rustc_errors::{
     Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
-    TerminalUrl, fallback_fluent_bundle,
+    LintEmitter, TerminalUrl, fallback_fluent_bundle,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -223,6 +224,20 @@ pub struct Session {
     pub invocation_temp: Option<String>,
 }
 
+impl LintEmitter for &'_ Session {
+    type Id = NodeId;
+
+    fn emit_node_span_lint(
+        self,
+        lint: &'static rustc_lint_defs::Lint,
+        node_id: Self::Id,
+        span: impl Into<rustc_errors::MultiSpan>,
+        decorator: impl for<'a> rustc_errors::LintDiagnostic<'a, ()> + DynSend + 'static,
+    ) {
+        self.psess.buffer_lint(lint, span, node_id, decorator);
+    }
+}
+
 #[derive(Clone, Copy)]
 pub enum CodegenUnits {
     /// Specified by the user. In this case we try fairly hard to produce the
diff --git a/tests/ui/lint/unused/unused_attributes-must_use.fixed b/tests/ui/lint/unused/unused_attributes-must_use.fixed
index 2e800cbff3f..6bc63c8ee5b 100644
--- a/tests/ui/lint/unused/unused_attributes-must_use.fixed
+++ b/tests/ui/lint/unused/unused_attributes-must_use.fixed
@@ -66,6 +66,8 @@ extern "Rust" {
 }
 
  //~ ERROR unused attribute
+//~^ ERROR `#[must_use]` attribute cannot be used on macro calls
+//~| WARN this was previously accepted by the compiler but is being phased out
 global_asm!("");
 
  //~ ERROR attribute cannot be used on
diff --git a/tests/ui/lint/unused/unused_attributes-must_use.rs b/tests/ui/lint/unused/unused_attributes-must_use.rs
index c41c6c1d706..3c0d76e619f 100644
--- a/tests/ui/lint/unused/unused_attributes-must_use.rs
+++ b/tests/ui/lint/unused/unused_attributes-must_use.rs
@@ -66,6 +66,8 @@ extern "Rust" {
 }
 
 #[must_use] //~ ERROR unused attribute
+//~^ ERROR `#[must_use]` attribute cannot be used on macro calls
+//~| WARN this was previously accepted by the compiler but is being phased out
 global_asm!("");
 
 #[must_use] //~ ERROR attribute cannot be used on
diff --git a/tests/ui/lint/unused/unused_attributes-must_use.stderr b/tests/ui/lint/unused/unused_attributes-must_use.stderr
index 03baea3a004..231d799057c 100644
--- a/tests/ui/lint/unused/unused_attributes-must_use.stderr
+++ b/tests/ui/lint/unused/unused_attributes-must_use.stderr
@@ -1,20 +1,29 @@
-error: unused attribute `must_use`
+error: `#[must_use]` attribute cannot be used on macro calls
   --> $DIR/unused_attributes-must_use.rs:68:1
    |
 LL | #[must_use]
    | ^^^^^^^^^^^
    |
-note: the built-in attribute `must_use` will be ignored, since it's applied to the macro invocation `global_asm`
-  --> $DIR/unused_attributes-must_use.rs:69:1
-   |
-LL | global_asm!("");
-   | ^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 note: the lint level is defined here
   --> $DIR/unused_attributes-must_use.rs:4:9
    |
 LL | #![deny(unused_attributes, unused_must_use)]
    |         ^^^^^^^^^^^^^^^^^
 
+error: unused attribute `must_use`
+  --> $DIR/unused_attributes-must_use.rs:68:1
+   |
+LL | #[must_use]
+   | ^^^^^^^^^^^
+   |
+note: the built-in attribute `must_use` will be ignored, since it's applied to the macro invocation `global_asm`
+  --> $DIR/unused_attributes-must_use.rs:71:1
+   |
+LL | global_asm!("");
+   | ^^^^^^^^^^
+
 error: `#[must_use]` attribute cannot be used on extern crates
   --> $DIR/unused_attributes-must_use.rs:7:1
    |
@@ -88,7 +97,7 @@ LL |     #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on type aliases
-  --> $DIR/unused_attributes-must_use.rs:71:1
+  --> $DIR/unused_attributes-must_use.rs:73:1
    |
 LL | #[must_use]
    | ^^^^^^^^^^^
@@ -97,7 +106,7 @@ LL | #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on function params
-  --> $DIR/unused_attributes-must_use.rs:75:8
+  --> $DIR/unused_attributes-must_use.rs:77:8
    |
 LL | fn qux<#[must_use] T>(_: T) {}
    |        ^^^^^^^^^^^
@@ -106,7 +115,7 @@ LL | fn qux<#[must_use] T>(_: T) {}
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on associated consts
-  --> $DIR/unused_attributes-must_use.rs:80:5
+  --> $DIR/unused_attributes-must_use.rs:82:5
    |
 LL |     #[must_use]
    |     ^^^^^^^^^^^
@@ -115,7 +124,7 @@ LL |     #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on associated types
-  --> $DIR/unused_attributes-must_use.rs:83:5
+  --> $DIR/unused_attributes-must_use.rs:85:5
    |
 LL |     #[must_use]
    |     ^^^^^^^^^^^
@@ -124,7 +133,7 @@ LL |     #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on trait impl blocks
-  --> $DIR/unused_attributes-must_use.rs:93:1
+  --> $DIR/unused_attributes-must_use.rs:95:1
    |
 LL | #[must_use]
    | ^^^^^^^^^^^
@@ -133,7 +142,7 @@ LL | #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on trait methods in impl blocks
-  --> $DIR/unused_attributes-must_use.rs:98:5
+  --> $DIR/unused_attributes-must_use.rs:100:5
    |
 LL |     #[must_use]
    |     ^^^^^^^^^^^
@@ -142,7 +151,7 @@ LL |     #[must_use]
    = help: `#[must_use]` can be applied to data types, functions, unions, required trait methods, provided trait methods, inherent methods, foreign functions, and traits
 
 error: `#[must_use]` attribute cannot be used on trait aliases
-  --> $DIR/unused_attributes-must_use.rs:105:1
+  --> $DIR/unused_attributes-must_use.rs:107:1
    |
 LL | #[must_use]
    | ^^^^^^^^^^^
@@ -151,7 +160,7 @@ LL | #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on macro defs
-  --> $DIR/unused_attributes-must_use.rs:109:1
+  --> $DIR/unused_attributes-must_use.rs:111:1
    |
 LL | #[must_use]
    | ^^^^^^^^^^^
@@ -160,7 +169,7 @@ LL | #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on statements
-  --> $DIR/unused_attributes-must_use.rs:118:5
+  --> $DIR/unused_attributes-must_use.rs:120:5
    |
 LL |     #[must_use]
    |     ^^^^^^^^^^^
@@ -169,7 +178,7 @@ LL |     #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on closures
-  --> $DIR/unused_attributes-must_use.rs:123:13
+  --> $DIR/unused_attributes-must_use.rs:125:13
    |
 LL |     let x = #[must_use]
    |             ^^^^^^^^^^^
@@ -178,7 +187,7 @@ LL |     let x = #[must_use]
    = help: `#[must_use]` can be applied to methods, data types, functions, unions, foreign functions, and traits
 
 error: `#[must_use]` attribute cannot be used on match arms
-  --> $DIR/unused_attributes-must_use.rs:146:9
+  --> $DIR/unused_attributes-must_use.rs:148:9
    |
 LL |         #[must_use]
    |         ^^^^^^^^^^^
@@ -187,7 +196,7 @@ LL |         #[must_use]
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on struct fields
-  --> $DIR/unused_attributes-must_use.rs:155:28
+  --> $DIR/unused_attributes-must_use.rs:157:28
    |
 LL |     let s = PatternField { #[must_use]  foo: 123 };
    |                            ^^^^^^^^^^^
@@ -196,7 +205,7 @@ LL |     let s = PatternField { #[must_use]  foo: 123 };
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: `#[must_use]` attribute cannot be used on pattern fields
-  --> $DIR/unused_attributes-must_use.rs:157:24
+  --> $DIR/unused_attributes-must_use.rs:159:24
    |
 LL |     let PatternField { #[must_use] foo } = s;
    |                        ^^^^^^^^^^^
@@ -205,7 +214,7 @@ LL |     let PatternField { #[must_use] foo } = s;
    = help: `#[must_use]` can be applied to functions, data types, unions, and traits
 
 error: unused `X` that must be used
-  --> $DIR/unused_attributes-must_use.rs:128:5
+  --> $DIR/unused_attributes-must_use.rs:130:5
    |
 LL |     X;
    |     ^
@@ -221,7 +230,7 @@ LL |     let _ = X;
    |     +++++++
 
 error: unused `Y` that must be used
-  --> $DIR/unused_attributes-must_use.rs:129:5
+  --> $DIR/unused_attributes-must_use.rs:131:5
    |
 LL |     Y::Z;
    |     ^^^^
@@ -232,7 +241,7 @@ LL |     let _ = Y::Z;
    |     +++++++
 
 error: unused `U` that must be used
-  --> $DIR/unused_attributes-must_use.rs:130:5
+  --> $DIR/unused_attributes-must_use.rs:132:5
    |
 LL |     U { unit: () };
    |     ^^^^^^^^^^^^^^
@@ -243,7 +252,7 @@ LL |     let _ = U { unit: () };
    |     +++++++
 
 error: unused return value of `U::method` that must be used
-  --> $DIR/unused_attributes-must_use.rs:131:5
+  --> $DIR/unused_attributes-must_use.rs:133:5
    |
 LL |     U::method();
    |     ^^^^^^^^^^^
@@ -254,7 +263,7 @@ LL |     let _ = U::method();
    |     +++++++
 
 error: unused return value of `foo` that must be used
-  --> $DIR/unused_attributes-must_use.rs:132:5
+  --> $DIR/unused_attributes-must_use.rs:134:5
    |
 LL |     foo();
    |     ^^^^^
@@ -265,7 +274,7 @@ LL |     let _ = foo();
    |     +++++++
 
 error: unused return value of `foreign_foo` that must be used
-  --> $DIR/unused_attributes-must_use.rs:135:9
+  --> $DIR/unused_attributes-must_use.rs:137:9
    |
 LL |         foreign_foo();
    |         ^^^^^^^^^^^^^
@@ -276,7 +285,7 @@ LL |         let _ = foreign_foo();
    |         +++++++
 
 error: unused return value of `Use::get_four` that must be used
-  --> $DIR/unused_attributes-must_use.rs:143:5
+  --> $DIR/unused_attributes-must_use.rs:145:5
    |
 LL |     ().get_four();
    |     ^^^^^^^^^^^^^
@@ -286,5 +295,5 @@ help: use `let _ = ...` to ignore the resulting value
 LL |     let _ = ().get_four();
    |     +++++++
 
-error: aborting due to 29 previous errors
+error: aborting due to 30 previous errors