From 2db126d651b6803c9b37fd021376ce6e5dd5a09e Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 1 Mar 2025 22:27:16 +0000 Subject: Include whitespace in "remove `|`" suggestion and make it hidden --- compiler/rustc_errors/src/diagnostic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 96c7ba6ed27..0034a30be08 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1112,7 +1112,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { .map(|snippet| { debug_assert!( !(sp.is_empty() && snippet.is_empty()), - "Span must not be empty and have no suggestion" + "Span `{sp:?}` must not be empty and have no suggestion" ); Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] } }) -- cgit 1.4.1-3-g733a5 From cf1a1b7b76a937afae997594d931b64956e74a4f Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sat, 9 Aug 2025 15:47:20 +0800 Subject: Use `eq_ignore_ascii_case` to avoid heap alloc in `detect_confuse_type` Signed-off-by: xizheyin --- compiler/rustc_errors/src/emitter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 84970e7c162..652e2ac63e6 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -3546,7 +3546,7 @@ pub fn detect_confusion_type(sm: &SourceMap, suggested: &str, sp: Span) -> Confu for (f, s) in iter::zip(found.chars(), suggested.chars()) { if f != s { - if f.to_lowercase().to_string() == s.to_lowercase().to_string() { + if f.eq_ignore_ascii_case(&s) { // Check for case differences (any character that differs only in case) if ascii_confusables.contains(&f) || ascii_confusables.contains(&s) { has_case_diff = true; -- cgit 1.4.1-3-g733a5 From 1aa5668d20d53976860d141a66aa727e425e1079 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 28 Jul 2025 02:01:46 +0000 Subject: Point at the `Fn()` or `FnMut()` bound that coerced a closure, which caused a move error When encountering a move error involving a closure because the captured value isn't `Copy`, and the obligation comes from a bound on a type parameter that requires `Fn` or `FnMut`, we point at it and explain that an `FnOnce` wouldn't cause the move error. ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:15:25 | 14 | fn do_stuff(foo: Option) { | --- ----------- move occurs because `foo` has type `Option`, which does not implement the `Copy` trait | | | captured outer variable 15 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 16 | if foo.map_or(false, |f| f.foo()) { | --- variable moved due to use in coroutine | help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once --> f111.rs:12:53 | 12 | fn require_fn_trait>(_: impl Fn() -> F) {} | ^^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | 16 | if foo.clone().map_or(false, |f| f.foo()) { | ++++++++ ``` --- .../rustc_borrowck/src/diagnostics/move_errors.rs | 40 +++++- compiler/rustc_errors/src/diagnostic.rs | 7 +- tests/ui/borrowck/borrowck-in-static.stderr | 1 + tests/ui/borrowck/borrowck-move-by-capture.stderr | 5 + tests/ui/borrowck/issue-103624.stderr | 5 + .../borrowck/issue-87456-point-to-closure.stderr | 5 + ...res-move-upvar-from-non-once-ref-closure.stderr | 5 + tests/ui/issues/issue-4335.stderr | 1 + ...-type-move-out-of-closure-env-issue-1965.stderr | 5 + .../issue-52663-span-decl-captured-variable.stderr | 5 + .../borrowck-call-is-borrow-issue-12224.stderr | 1 + .../dont-suggest-ref/move-into-closure.rs | 21 +++ .../dont-suggest-ref/move-into-closure.stderr | 147 ++++++++++++++++++--- tests/ui/suggestions/option-content-move2.stderr | 11 ++ tests/ui/suggestions/option-content-move3.stderr | 12 ++ .../unboxed-closure-illegal-move.stderr | 22 +++ 16 files changed, 268 insertions(+), 25 deletions(-) (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 1067f1e40ef..b24cb012d2b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -507,12 +507,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let closure_span = tcx.def_span(def_id); + let mut clause_span = DUMMY_SP; + let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); + if let Some(closure_def_id) = def_id.as_local() + && let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) + && let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Call(callee, _) = parent.kind + && let Some(ty) = typck_result.node_type_opt(callee.hir_id) + && let ty::FnDef(fn_def_id, args) = ty.kind() + && let predicates = tcx.predicates_of(fn_def_id).instantiate(tcx, args) + && let Some((_, span)) = + predicates.predicates.iter().zip(predicates.spans.iter()).find( + |(pred, _)| match pred.as_trait_clause() { + Some(clause) + if let ty::Closure(clause_closure_def_id, _) = + clause.self_ty().skip_binder().kind() + && clause_closure_def_id == def_id + && (tcx.lang_items().fn_mut_trait() + == Some(clause.def_id()) + || tcx.lang_items().fn_trait() + == Some(clause.def_id())) => + { + // Found `` + true + } + _ => false, + }, + ) + { + // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which + // could be changed to `FnOnce()` to avoid the move error. + clause_span = *span; + } + self.cannot_move_out_of(span, &place_description) .with_span_label(upvar_span, "captured outer variable") .with_span_label( closure_span, format!("captured by this `{closure_kind}` closure"), ) + .with_span_help( + clause_span, + "`Fn` and `FnMut` closures require captured values to be able to be \ + consumed multiple times, but an `FnOnce` consume them only once", + ) } _ => { let source = self.borrowed_content_source(deref_base); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5a5563c7bb2..98be37fd84b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -847,17 +847,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + with_fn! { with_span_help, /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] - pub fn span_help>( + pub fn span_help( &mut self, - sp: S, + sp: impl Into, msg: impl Into, ) -> &mut Self { self.sub(Level::Help, msg, sp.into()); self - } + } } /// Disallow attaching suggestions to this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 9bcf64dd62e..d85f6f5fdd5 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -10,6 +10,7 @@ LL | Box::new(|| x) | | | captured by this `Fn` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | Box::new(|| x.clone()) diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index 732af1593d6..e9e05440766 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -12,6 +12,11 @@ LL | let _h = to_fn_once(move || -> isize { *bar }); | | | `bar` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/borrowck-move-by-capture.rs:3:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index af65deb16dc..ef022808886 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -13,6 +13,11 @@ LL | LL | self.b; | ^^^^^^ `self.b` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-103624.rs:7:36 + | +LL | async fn spawn_blocking(f: impl (Fn() -> T) + Send + Sync + 'static) -> T { + | ^^^^^^^^^^^ note: if `StructB` implemented `Clone`, you could clone the value --> $DIR/issue-103624.rs:23:1 | diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index a0c7cac2add..043e336cd86 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -10,6 +10,11 @@ LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-87456-point-to-closure.rs:3:24 + | +LL | fn take_mut(_val: impl FnMut()) {} + | ^^^^^^^ help: consider borrowing here | LL | let _foo: String = &val; diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 177e9c8d248..d3333041310 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -10,6 +10,11 @@ LL | y.into_iter(); | | | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:5:28 + | +LL | fn call(f: F) where F : Fn() { + | ^^^^ note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index 42ac6322564..b6d8f086163 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -10,6 +10,7 @@ LL | id(Box::new(|| *v)) | | | captured by this `FnMut` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: if `T` implemented `Clone`, you could clone the value --> $DIR/issue-4335.rs:5:10 | diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 51d0f85c031..dfc983bf487 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -10,6 +10,11 @@ LL | let _f = to_fn(|| test(i)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:3:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let _f = to_fn(|| test(i.clone())); diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 57546037006..7f9a8e50dae 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -10,6 +10,11 @@ LL | expect_fn(|| drop(x.0)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-52663-span-decl-captured-variable.rs:1:33 + | +LL | fn expect_fn(f: F) where F : Fn() { + | ^^^^ help: consider cloning the value if the performance cost is acceptable | LL | expect_fn(|| drop(x.0.clone())); diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index f37dc320fa3..8081f7b3a8b 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -44,6 +44,7 @@ LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | foo(f.clone()); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs index 44eac3691a3..0154810f564 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs @@ -11,8 +11,29 @@ struct X(Y); struct Y; fn consume_fn(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures fn consume_fnmut(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures pub fn main() { } diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index edda2cbc735..d9ed25983d6 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:28:21 + --> $DIR/move-into-closure.rs:49:21 | LL | let x = X(Y); | - captured outer variable @@ -12,13 +12,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:31:34 + --> $DIR/move-into-closure.rs:52:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -32,13 +37,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:34:37 + --> $DIR/move-into-closure.rs:55:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -52,13 +62,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:37:15 + --> $DIR/move-into-closure.rs:58:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -75,13 +90,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:43:15 + --> $DIR/move-into-closure.rs:64:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -98,13 +118,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:51:25 + --> $DIR/move-into-closure.rs:72:25 | LL | let x = X(Y); | - captured outer variable @@ -118,13 +143,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:54:38 + --> $DIR/move-into-closure.rs:75:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -138,13 +168,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:57:41 + --> $DIR/move-into-closure.rs:78:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -158,13 +193,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:60:15 + --> $DIR/move-into-closure.rs:81:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -181,13 +221,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:66:15 + --> $DIR/move-into-closure.rs:87:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -204,13 +249,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:85:21 + --> $DIR/move-into-closure.rs:106:21 | LL | let x = X(Y); | - captured outer variable @@ -223,13 +273,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:88:34 + --> $DIR/move-into-closure.rs:109:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -243,13 +298,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:91:37 + --> $DIR/move-into-closure.rs:112:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -263,13 +323,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:94:15 + --> $DIR/move-into-closure.rs:115:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -286,13 +351,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:100:15 + --> $DIR/move-into-closure.rs:121:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -309,13 +379,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:108:25 + --> $DIR/move-into-closure.rs:129:25 | LL | let x = X(Y); | - captured outer variable @@ -329,13 +404,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:111:38 + --> $DIR/move-into-closure.rs:132:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -349,13 +429,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:114:41 + --> $DIR/move-into-closure.rs:135:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -369,13 +454,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:117:15 + --> $DIR/move-into-closure.rs:138:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -392,13 +482,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:123:15 + --> $DIR/move-into-closure.rs:144:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -415,13 +510,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:130:15 + --> $DIR/move-into-closure.rs:151:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -438,6 +538,11 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index c73e874b403..c8aa6667b58 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -14,6 +14,11 @@ LL | LL | var = Some(NotCopyable); | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move2.rs:1:1 | @@ -38,6 +43,12 @@ LL | move || { LL | LL | var = Some(NotCopyableButCloneable); | --- variable moved due to use in closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index 68c52352a65..2c9a86c036b 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -9,6 +9,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -37,6 +38,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -57,6 +63,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider borrowing here | LL | let x = &var; @@ -77,6 +84,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ { diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 8d9a61cb681..9d87402a15b 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -10,6 +10,11 @@ LL | let f = to_fn(|| drop(x)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn(|| drop(x.clone())); @@ -27,6 +32,11 @@ LL | let f = to_fn_mut(|| drop(x)); | | | captured by this `FnMut` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn_mut(|| drop(x.clone())); @@ -43,6 +53,12 @@ LL | let f = to_fn(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `Fn` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -55,6 +71,12 @@ LL | let f = to_fn_mut(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `FnMut` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 51bccdd1ab0802dd5a55bd06e956c8d547bdec2d Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 10 Aug 2025 09:44:39 +0200 Subject: Port `#[custom_mir(..)]` to the new attribute system --- compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + .../rustc_attr_parsing/src/attributes/prototype.rs | 140 +++++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 + compiler/rustc_errors/src/diagnostic_impls.rs | 23 ++++ compiler/rustc_hir/src/attrs/data_structures.rs | 19 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_middle/src/mir/mod.rs | 42 ------- compiler/rustc_mir_build/src/builder/custom/mod.rs | 65 +++++----- compiler/rustc_mir_build/src/builder/mod.rs | 9 +- compiler/rustc_mir_build/src/check_unsafety.rs | 5 +- compiler/rustc_mir_build/src/thir/cx/mod.rs | 10 +- compiler/rustc_passes/messages.ftl | 10 ++ compiler/rustc_passes/src/check_attr.rs | 51 +++++++- compiler/rustc_passes/src/errors.rs | 23 ++++ compiler/rustc_span/src/symbol.rs | 8 ++ 15 files changed, 318 insertions(+), 91 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/prototype.rs (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index ed5d1d92b8c..3d6e26a24b8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -43,6 +43,7 @@ pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; pub(crate) mod path; pub(crate) mod proc_macro_attrs; +pub(crate) mod prototype; pub(crate) mod repr; pub(crate) mod rustc_internal; pub(crate) mod semantics; diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs new file mode 100644 index 00000000000..fb1e47298b4 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -0,0 +1,140 @@ +//! Attributes that are only used on function prototypes. + +use rustc_feature::{AttributeTemplate, template}; +use rustc_hir::Target; +use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase}; +use rustc_span::{Span, Symbol, sym}; + +use super::{AttributeOrder, OnDuplicate}; +use crate::attributes::SingleAttributeParser; +use crate::context::{AcceptContext, AllowedTargets, MaybeWarn, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct CustomMirParser; + +impl SingleAttributeParser for CustomMirParser { + const PATH: &[rustc_span::Symbol] = &[sym::custom_mir]; + + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[MaybeWarn::Allow(Target::Fn)]); + + const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let mut dialect = None; + let mut phase = None; + let mut failed = false; + + for item in list.mixed() { + let Some(meta_item) = item.meta_item() else { + cx.expected_name_value(item.span(), None); + failed = true; + break; + }; + + if let Some(arg) = meta_item.word_is(sym::dialect) { + extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed); + } else if let Some(arg) = meta_item.word_is(sym::phase) { + extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed); + } else if let Some(word) = meta_item.path().word() { + let word = word.to_string(); + cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]); + failed = true; + } else { + cx.expected_name_value(meta_item.span(), None); + failed = true; + }; + } + + let dialect = parse_dialect(cx, dialect, &mut failed); + let phase = parse_phase(cx, phase, &mut failed); + + if failed { + return None; + } + + Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span)) + } +} + +fn extract_value( + cx: &mut AcceptContext<'_, '_, S>, + key: Symbol, + arg: &ArgParser<'_>, + span: Span, + out_val: &mut Option<(Symbol, Span)>, + failed: &mut bool, +) { + if out_val.is_some() { + cx.duplicate_key(span, key); + *failed = true; + return; + } + + let Some(val) = arg.name_value() else { + cx.expected_single_argument(arg.span().unwrap_or(span)); + *failed = true; + return; + }; + + let Some(value_sym) = val.value_as_str() else { + cx.expected_string_literal(val.value_span, Some(val.value_as_lit())); + *failed = true; + return; + }; + + *out_val = Some((value_sym, val.value_span)); +} + +fn parse_dialect( + cx: &mut AcceptContext<'_, '_, S>, + dialect: Option<(Symbol, Span)>, + failed: &mut bool, +) -> Option<(MirDialect, Span)> { + let (dialect, span) = dialect?; + + let dialect = match dialect { + sym::analysis => MirDialect::Analysis, + sym::built => MirDialect::Built, + sym::runtime => MirDialect::Runtime, + + _ => { + cx.expected_specific_argument(span, vec!["analysis", "built", "runtime"]); + *failed = true; + return None; + } + }; + + Some((dialect, span)) +} + +fn parse_phase( + cx: &mut AcceptContext<'_, '_, S>, + phase: Option<(Symbol, Span)>, + failed: &mut bool, +) -> Option<(MirPhase, Span)> { + let (phase, span) = phase?; + + let phase = match phase { + sym::initial => MirPhase::Initial, + sym::post_cleanup => MirPhase::PostCleanup, + sym::optimized => MirPhase::Optimized, + + _ => { + cx.expected_specific_argument(span, vec!["initial", "post-cleanup", "optimized"]); + *failed = true; + return None; + } + }; + + Some((phase, span)) +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index bebe3350c4e..d1d1ea43b2f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -46,6 +46,7 @@ use crate::attributes::path::PathParser as PathAttributeParser; use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; +use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, @@ -167,6 +168,7 @@ attribute_parsers!( // tidy-alphabetical-start Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index eca5806fac5..698b6b8d504 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -9,6 +9,7 @@ use rustc_abi::TargetDataLayoutErrors; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; use rustc_hir::RustcVersion; +use rustc_hir::attrs::{MirDialect, MirPhase}; use rustc_macros::Subdiagnostic; use rustc_span::edition::Edition; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; @@ -312,6 +313,28 @@ impl IntoDiagArg for ExprPrecedence { } } +impl IntoDiagArg for MirDialect { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let arg = match self { + MirDialect::Analysis => "analysis", + MirDialect::Built => "built", + MirDialect::Runtime => "runtime", + }; + DiagArgValue::Str(Cow::Borrowed(arg)) + } +} + +impl IntoDiagArg for MirPhase { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let arg = match self { + MirPhase::Initial => "initial", + MirPhase::PostCleanup => "post-cleanup", + MirPhase::Optimized => "optimized", + }; + DiagArgValue::Str(Cow::Borrowed(arg)) + } +} + #[derive(Clone)] pub struct DiagSymbolList(Vec); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 510fc832978..31715955ed3 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -205,6 +205,22 @@ pub enum Linkage { WeakODR, } +#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum MirDialect { + Analysis, + Built, + Runtime, +} + +#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum MirPhase { + Initial, + PostCleanup, + Optimized, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -324,6 +340,9 @@ pub enum AttributeKind { /// Represents `#[coverage(..)]`. Coverage(Span, CoverageAttrKind), + /// Represents `#[custom_mir]`. + CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), + ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 84a975523f2..defabdccc02 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -31,6 +31,7 @@ impl AttributeKind { ConstTrait(..) => No, Coroutine(..) => No, Coverage(..) => No, + CustomMir(_, _, _) => Yes, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c55c7fc6002..c977e5329c2 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -115,48 +115,6 @@ impl MirPhase { MirPhase::Runtime(runtime_phase) => (3, 1 + runtime_phase as usize), } } - - /// Parses a `MirPhase` from a pair of strings. Panics if this isn't possible for any reason. - pub fn parse(dialect: String, phase: Option) -> Self { - match &*dialect.to_ascii_lowercase() { - "built" => { - assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); - MirPhase::Built - } - "analysis" => Self::Analysis(AnalysisPhase::parse(phase)), - "runtime" => Self::Runtime(RuntimePhase::parse(phase)), - _ => bug!("Unknown MIR dialect: '{}'", dialect), - } - } -} - -impl AnalysisPhase { - pub fn parse(phase: Option) -> Self { - let Some(phase) = phase else { - return Self::Initial; - }; - - match &*phase.to_ascii_lowercase() { - "initial" => Self::Initial, - "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, - _ => bug!("Unknown analysis phase: '{}'", phase), - } - } -} - -impl RuntimePhase { - pub fn parse(phase: Option) -> Self { - let Some(phase) = phase else { - return Self::Initial; - }; - - match &*phase.to_ascii_lowercase() { - "initial" => Self::Initial, - "post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup, - "optimized" => Self::Optimized, - _ => bug!("Unknown runtime phase: '{}'", phase), - } - } } /// Where a specific `mir::Body` comes from. diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index 902a6e7f115..792ad6d782c 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -19,10 +19,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{Attribute, HirId}; +use rustc_hir::{HirId, attrs}; use rustc_index::{IndexSlice, IndexVec}; +use rustc_middle::bug; use rustc_middle::mir::*; -use rustc_middle::span_bug; use rustc_middle::thir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -39,7 +39,8 @@ pub(super) fn build_custom_mir<'tcx>( return_ty: Ty<'tcx>, return_ty_span: Span, span: Span, - attr: &Attribute, + dialect: Option, + phase: Option, ) -> Body<'tcx> { let mut body = Body { basic_blocks: BasicBlocks::new(IndexVec::new()), @@ -72,7 +73,7 @@ pub(super) fn build_custom_mir<'tcx>( inlined_parent_scope: None, local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), }); - body.injection_phase = Some(parse_attribute(attr)); + body.injection_phase = Some(parse_attribute(dialect, phase)); let mut pctxt = ParseCtxt { tcx, @@ -98,40 +99,38 @@ pub(super) fn build_custom_mir<'tcx>( body } -fn parse_attribute(attr: &Attribute) -> MirPhase { - let meta_items = attr.meta_item_list().unwrap(); - let mut dialect: Option = None; - let mut phase: Option = None; - - // Not handling errors properly for this internal attribute; will just abort on errors. - for nested in meta_items { - let name = nested.name().unwrap(); - let value = nested.value_str().unwrap().as_str().to_string(); - match name.as_str() { - "dialect" => { - assert!(dialect.is_none()); - dialect = Some(value); - } - "phase" => { - assert!(phase.is_none()); - phase = Some(value); - } - other => { - span_bug!( - nested.span(), - "Unexpected key while parsing custom_mir attribute: '{}'", - other - ); - } - } - } - +/// Turns the arguments passed to `#[custom_mir(..)]` into a proper +/// [`MirPhase`]. Panics if this isn't possible for any reason. +fn parse_attribute(dialect: Option, phase: Option) -> MirPhase { let Some(dialect) = dialect else { + // Caught during attribute checking. assert!(phase.is_none()); return MirPhase::Built; }; - MirPhase::parse(dialect, phase) + match dialect { + attrs::MirDialect::Built => { + // Caught during attribute checking. + assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); + MirPhase::Built + } + attrs::MirDialect::Analysis => match phase { + None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial), + + Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup), + + Some(attrs::MirPhase::Optimized) => { + // Caught during attribute checking. + bug!("`optimized` dialect is not compatible with the `analysis` dialect") + } + }, + + attrs::MirDialect::Runtime => match phase { + None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial), + Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup), + Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized), + }, + } } struct ParseCtxt<'a, 'tcx> { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 855cd2f3bc0..9570760f943 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -11,9 +11,10 @@ use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -479,8 +480,7 @@ fn construct_fn<'tcx>( ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"), }; - if let Some(custom_mir_attr) = - tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir)) + if let Some((dialect, phase)) = find_attr!(tcx.hir_attrs(fn_id), AttributeKind::CustomMir(dialect, phase, _) => (dialect, phase)) { return custom::build_custom_mir( tcx, @@ -492,7 +492,8 @@ fn construct_fn<'tcx>( return_ty, return_ty_span, span_with_body, - custom_mir_attr, + dialect.as_ref().map(|(d, _)| *d), + phase.as_ref().map(|(p, _)| *p), ); } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 0b6b36640e9..cdab785e842 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,8 +5,9 @@ use std::ops::Bound; use rustc_ast::AsmMacro; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::DiagArgValue; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; @@ -1157,7 +1158,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Closures and inline consts are handled by their owner, if it has a body assert!(!tcx.is_typeck_child(def.to_def_id())); // Also, don't safety check custom MIR - if tcx.has_attr(def, sym::custom_mir) { + if find_attr!(tcx.get_all_attrs(def), AttributeKind::CustomMir(..) => ()).is_some() { return; } diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 24d4136c642..9657c4dc839 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -4,11 +4,11 @@ use rustc_data_structures::steal::Steal; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::HirId; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_hir::{self as hir, HirId, find_attr}; use rustc_middle::bug; use rustc_middle::middle::region; use rustc_middle::thir::*; @@ -111,10 +111,8 @@ impl<'tcx> ThirBuildCx<'tcx> { typeck_results, rvalue_scopes: &typeck_results.rvalue_scopes, body_owner: def.to_def_id(), - apply_adjustments: tcx - .hir_attrs(hir_id) - .iter() - .all(|attr| !attr.has_name(rustc_span::sym::custom_mir)), + apply_adjustments: + !find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(), } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7481b0ea960..f7a5ba8194b 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -74,6 +74,16 @@ passes_const_stable_not_stable = attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]` .label = attribute specified here +passes_custom_mir_incompatible_dialect_and_phase = + The {$dialect} dialect is not compatible with the {$phase} phase + .dialect_span = this dialect... + .phase_span = ... is not compatible with this phase + +passes_custom_mir_phase_requires_dialect = + `dialect` key required + .phase_span = `phase` argument requires a `dialect` argument + + passes_dead_codes = { $multiple -> *[true] multiple {$descr}s are diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0e28c51e981..3c329d20700 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -18,7 +18,7 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, }; -use rustc_hir::attrs::{AttributeKind, InlineAttr, ReprAttr}; +use rustc_hir::attrs::{AttributeKind, InlineAttr, MirDialect, MirPhase, ReprAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -197,6 +197,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::MustUse { span, .. }) => { self.check_must_use(hir_id, *span, target) } + &Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => { + self.check_custom_mir(dialect, phase, attr_span) + } Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -248,7 +251,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Coroutine(..) | AttributeKind::Linkage(..), ) => { /* do nothing */ } - Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -331,8 +333,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::panic_handler | sym::lang | sym::needs_allocator - | sym::default_lib_allocator - | sym::custom_mir, + | sym::default_lib_allocator, .. ] => {} [name, rest@..] => { @@ -2113,6 +2114,48 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span }); }; } + + fn check_custom_mir( + &self, + dialect: Option<(MirDialect, Span)>, + phase: Option<(MirPhase, Span)>, + attr_span: Span, + ) { + let Some((dialect, dialect_span)) = dialect else { + if let Some((_, phase_span)) = phase { + self.dcx() + .emit_err(errors::CustomMirPhaseRequiresDialect { attr_span, phase_span }); + } + return; + }; + + match dialect { + MirDialect::Analysis => { + if let Some((MirPhase::Optimized, phase_span)) = phase { + self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase { + dialect, + phase: MirPhase::Optimized, + attr_span, + dialect_span, + phase_span, + }); + } + } + + MirDialect::Built => { + if let Some((phase, phase_span)) = phase { + self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase { + dialect, + phase, + attr_span, + dialect_span, + phase_span, + }); + } + } + MirDialect::Runtime => {} + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c5d5155d0e5..f8ecf10714a 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -7,6 +7,7 @@ use rustc_errors::{ MultiSpan, Subdiagnostic, }; use rustc_hir::Target; +use rustc_hir::attrs::{MirDialect, MirPhase}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{DUMMY_SP, Span, Symbol}; @@ -1570,3 +1571,25 @@ pub(crate) struct ReprAlignShouldBeAlign { pub span: Span, pub item: &'static str, } + +#[derive(Diagnostic)] +#[diag(passes_custom_mir_phase_requires_dialect)] +pub(crate) struct CustomMirPhaseRequiresDialect { + #[primary_span] + pub attr_span: Span, + #[label] + pub phase_span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_custom_mir_incompatible_dialect_and_phase)] +pub(crate) struct CustomMirIncompatibleDialectAndPhase { + pub dialect: MirDialect, + pub phase: MirPhase, + #[primary_span] + pub attr_span: Span, + #[label] + pub dialect_span: Span, + #[label] + pub phase_span: Span, +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 416ce27367e..12bb216b8d8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -446,6 +446,7 @@ symbols! { altivec, alu32, always, + analysis, and, and_then, anon, @@ -587,6 +588,7 @@ symbols! { btreemap_contains_key, btreemap_insert, btreeset_iter, + built, builtin_syntax, c, c_dash_variadic, @@ -851,6 +853,7 @@ symbols! { destructuring_assignment, diagnostic, diagnostic_namespace, + dialect, direct, discriminant_kind, discriminant_type, @@ -1207,6 +1210,7 @@ symbols! { infer_static_outlives_requirements, inherent_associated_types, inherit, + initial, inlateout, inline, inline_const, @@ -1542,6 +1546,7 @@ symbols! { opt_out_copy, optimize, optimize_attribute, + optimized, optin_builtin_traits, option, option_env, @@ -1623,6 +1628,7 @@ symbols! { pattern_types, permissions_from_mode, phantom_data, + phase, pic, pie, pin, @@ -1639,6 +1645,7 @@ symbols! { poll, poll_next, position, + post_cleanup: "post-cleanup", post_dash_lto: "post-lto", postfix_match, powerpc_target_feature, @@ -1802,6 +1809,7 @@ symbols! { roundf128, rt, rtm_target_feature, + runtime, rust, rust_2015, rust_2018, -- cgit 1.4.1-3-g733a5 From c3c2c23e0d96c76f11a307cf3c4cf14a86fd158b Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 3 Apr 2025 20:59:05 -0400 Subject: Extend `QueryStability` to handle `IntoIterator` implementations Fix adjacent code Fix duplicate warning; merge test into `tests/ui-fulldeps/internal-lints` Use `rustc_middle::ty::FnSig::inputs` Address two review comments - https://github.com/rust-lang/rust/pull/139345#discussion_r2109006991 - https://github.com/rust-lang/rust/pull/139345#discussion_r2109058588 Use `Instance::try_resolve` Import `rustc_middle::ty::Ty` as `Ty` rather than `MiddleTy` Simplify predicate handling Add more `#[allow(rustc::potential_query_instability)]` following rebase Remove two `#[allow(rustc::potential_query_instability)]` following rebase Address review comment Update compiler/rustc_lint/src/internal.rs Co-authored-by: lcnr --- compiler/rustc_codegen_ssa/src/target_features.rs | 1 + compiler/rustc_errors/src/emitter.rs | 4 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 1 + compiler/rustc_interface/src/interface.rs | 4 +- compiler/rustc_lint/src/internal.rs | 132 +++++++++++++-------- src/librustdoc/formats/cache.rs | 2 +- .../ui-fulldeps/internal-lints/query_stability.rs | 12 ++ .../internal-lints/query_stability.stderr | 18 ++- 8 files changed, 122 insertions(+), 52 deletions(-) (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7e4341a8236..b5aa50f4851 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -180,6 +180,7 @@ fn parse_rust_feature_flag<'a>( while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { if let Some(implied_features) = inverse_implied_features.get(&new_feature) { + #[allow(rustc::potential_query_instability)] new_features.extend(implied_features) } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 97c47fa9b9a..749bba5de12 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -17,7 +17,7 @@ use std::path::Path; use std::sync::Arc; use derive_setters::Setters; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_lexer; @@ -1853,7 +1853,7 @@ impl HumanEmitter { && line_idx + 1 == annotated_file.lines.len(), ); - let mut to_add = FxHashMap::default(); + let mut to_add = FxIndexMap::default(); for (depth, style) in depths { // FIXME(#120456) - is `swap_remove` correct? diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 334f57f9d62..6b57ced56eb 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -522,6 +522,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( match result { Success(body_named_matches) => { psess.gated_spans.merge(gated_spans_snapshot); + #[allow(rustc::potential_query_instability)] named_matches.extend(body_named_matches); return Ok((i, rule, named_matches)); } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c46e879b976..8f131f45bbd 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -285,7 +285,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch .expecteds .entry(name.name) .and_modify(|v| match v { - ExpectedValues::Some(v) if !values_any_specified => { + ExpectedValues::Some(v) if !values_any_specified => + { + #[allow(rustc::potential_query_instability)] v.extend(values.clone()) } ExpectedValues::Some(_) => *v = ExpectedValues::Any, diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 016ff17f5d7..e1fbe39222b 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,10 +1,10 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. -use rustc_hir::HirId; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; +use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_middle::ty::{self, ClauseKind, GenericArgsRef, PredicatePolarity, TraitPredicate, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{Span, sym}; @@ -56,25 +56,6 @@ impl LateLintPass<'_> for DefaultHashTypes { } } -/// Helper function for lints that check for expressions with calls and use typeck results to -/// get the `DefId` and `GenericArgsRef` of the function. -fn typeck_results_of_method_fn<'tcx>( - cx: &LateContext<'tcx>, - expr: &hir::Expr<'_>, -) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> { - match expr.kind { - hir::ExprKind::MethodCall(segment, ..) - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => - { - Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id))) - } - _ => match cx.typeck_results().node_type(expr.hir_id).kind() { - &ty::FnDef(def_id, args) => Some((expr.span, def_id, args)), - _ => None, - }, - } -} - declare_tool_lint! { /// The `potential_query_instability` lint detects use of methods which can lead to /// potential query instability, such as iterating over a `HashMap`. @@ -101,10 +82,12 @@ declare_tool_lint! { declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]); -impl LateLintPass<'_> for QueryStability { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; - if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args) +impl<'tcx> LateLintPass<'tcx> for QueryStability { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some((callee_def_id, span, generic_args, _recv, _args)) = + get_callee_span_generic_args_and_args(cx, expr) + && let Ok(Some(instance)) = + ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args) { let def_id = instance.def_id(); if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { @@ -113,7 +96,15 @@ impl LateLintPass<'_> for QueryStability { span, QueryInstability { query: cx.tcx.item_name(def_id) }, ); + } else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) { + let call_span = span.with_hi(expr.span.hi()); + cx.emit_span_lint( + POTENTIAL_QUERY_INSTABILITY, + call_span, + QueryInstability { query: sym::into_iter }, + ); } + if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) { cx.emit_span_lint( UNTRACKED_QUERY_INFORMATION, @@ -125,6 +116,64 @@ impl LateLintPass<'_> for QueryStability { } } +fn has_unstable_into_iter_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + generic_args: GenericArgsRef<'tcx>, +) -> bool { + let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else { + return false; + }; + let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else { + return false; + }; + let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args); + for (predicate, _) in predicates { + let ClauseKind::Trait(TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive }) = + predicate.kind().skip_binder() + else { + continue; + }; + // Does the function or method require any of its arguments to implement `IntoIterator`? + if trait_ref.def_id != into_iterator_def_id { + continue; + } + let Ok(Some(instance)) = + ty::Instance::try_resolve(cx.tcx, cx.typing_env(), into_iter_fn_def_id, trait_ref.args) + else { + continue; + }; + // Does the input type's `IntoIterator` implementation have the + // `rustc_lint_query_instability` attribute on its `into_iter` method? + if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) { + return true; + } + } + false +} + +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`, +/// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy +/// function, `get_callee_generic_args_and_args`. +fn get_callee_span_generic_args_and_args<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> { + if let ExprKind::Call(callee, args) = expr.kind + && let callee_ty = cx.typeck_results().expr_ty(callee) + && let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind() + { + return Some((*callee_def_id, callee.span, generic_args, None, args)); + } + if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + { + let generic_args = cx.typeck_results().node_args(expr.hir_id); + return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args)); + } + None +} + declare_tool_lint! { /// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::`, /// where `ty::` would suffice. @@ -461,33 +510,22 @@ declare_tool_lint! { declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); impl LateLintPass<'_> for Diagnostics { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| { let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra)); result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span))); result }; // Only check function calls and method calls. - let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind { - hir::ExprKind::Call(callee, args) => { - match cx.typeck_results().node_type(callee.hir_id).kind() { - &ty::FnDef(def_id, fn_gen_args) => { - (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false)) - } - _ => return, // occurs for fns passed as args - } - } - hir::ExprKind::MethodCall(_segment, _recv, args, _span) => { - let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr) - else { - return; - }; - let mut args = collect_args_tys_and_spans(args, true); - args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self` - (span, def_id, fn_gen_args, args) - } - _ => return, + let Some((def_id, span, fn_gen_args, recv, args)) = + get_callee_span_generic_args_and_args(cx, expr) + else { + return; }; + let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some()); + if let Some(recv) = recv { + arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); // dummy inserted for `self` + } Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args); Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans); @@ -496,7 +534,7 @@ impl LateLintPass<'_> for Diagnostics { impl Diagnostics { // Is the type `{D,Subd}iagMessage`? - fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool { + fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool { if let Some(adt_def) = ty.ty_adt_def() && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did()) && matches!(name, sym::DiagMessage | sym::SubdiagMessage) @@ -510,7 +548,7 @@ impl Diagnostics { fn untranslatable_diagnostic<'cx>( cx: &LateContext<'cx>, def_id: DefId, - arg_tys_and_spans: &[(MiddleTy<'cx>, Span)], + arg_tys_and_spans: &[(Ty<'cx>, Span)], ) { let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates; diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 918b292466d..359a35ec7fa 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -48,7 +48,7 @@ pub(crate) struct Cache { /// Similar to `paths`, but only holds external paths. This is only used for /// generating explicit hyperlinks to other crates. - pub(crate) external_paths: FxHashMap, ItemType)>, + pub(crate) external_paths: FxIndexMap, ItemType)>, /// Maps local `DefId`s of exported types to fully qualified paths. /// Unlike 'paths', this mapping ignores any renames that occur diff --git a/tests/ui-fulldeps/internal-lints/query_stability.rs b/tests/ui-fulldeps/internal-lints/query_stability.rs index 7b897fabd2d..9dc65250064 100644 --- a/tests/ui-fulldeps/internal-lints/query_stability.rs +++ b/tests/ui-fulldeps/internal-lints/query_stability.rs @@ -34,4 +34,16 @@ fn main() { //~^ ERROR using `values_mut` can result in unstable query results *val = *val + 10; } + + FxHashMap::::default().extend(x); + //~^ ERROR using `into_iter` can result in unstable query results +} + +fn hide_into_iter(x: impl IntoIterator) -> impl Iterator { + x.into_iter() +} + +fn take(map: std::collections::HashMap) { + _ = hide_into_iter(map); + //~^ ERROR using `into_iter` can result in unstable query results } diff --git a/tests/ui-fulldeps/internal-lints/query_stability.stderr b/tests/ui-fulldeps/internal-lints/query_stability.stderr index 43b156dc20a..a95ffd25a47 100644 --- a/tests/ui-fulldeps/internal-lints/query_stability.stderr +++ b/tests/ui-fulldeps/internal-lints/query_stability.stderr @@ -59,5 +59,21 @@ LL | for val in x.values_mut() { | = note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale -error: aborting due to 7 previous errors +error: using `into_iter` can result in unstable query results + --> $DIR/query_stability.rs:38:38 + | +LL | FxHashMap::::default().extend(x); + | ^^^^^^^^^ + | + = note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale + +error: using `into_iter` can result in unstable query results + --> $DIR/query_stability.rs:47:9 + | +LL | _ = hide_into_iter(map); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From b65fab62999e96acd3683826ffd4140091f185f2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 14 Aug 2025 00:28:44 -0700 Subject: Move `IntoDiagArg` earlier in the dependency chains `rustc_errors` depends on numerous crates, solely to implement its `IntoDiagArg` trait on types from those crates. Many crates depend on `rustc_errors`, and it's on the critical path. We can't swap things around to make all of those crates depend on `rustc_errors` instead, because `rustc_errors` would end up in dependency cycles. Instead, move `IntoDiagArg` into `rustc_error_messages`, which has far fewer dependencies, and then have most of these crates depend on `rustc_error_messages`. This allows `rustc_errors` to drop dependencies on several crates, including the large `rustc_target`. (This doesn't fully reduce dependency chains yet, as `rustc_errors` still depends on `rustc_hir` which depends on `rustc_target`. That will get fixed in a subsequent commit.) --- Cargo.lock | 10 +- compiler/rustc_abi/Cargo.toml | 2 + compiler/rustc_abi/src/extern_abi.rs | 3 + compiler/rustc_error_messages/Cargo.toml | 2 + .../rustc_error_messages/src/diagnostic_impls.rs | 205 +++++++++++++ compiler/rustc_error_messages/src/lib.rs | 53 ++++ compiler/rustc_errors/Cargo.toml | 4 - compiler/rustc_errors/src/codes.rs | 2 + compiler/rustc_errors/src/diagnostic.rs | 52 +--- compiler/rustc_errors/src/diagnostic_impls.rs | 326 +-------------------- compiler/rustc_errors/src/lib.rs | 18 +- compiler/rustc_hir/Cargo.toml | 1 + compiler/rustc_hir/src/attrs/data_structures.rs | 26 ++ compiler/rustc_hir/src/def.rs | 14 + compiler/rustc_hir/src/hir.rs | 13 +- compiler/rustc_hir/src/target.rs | 2 + compiler/rustc_hir/src/version.rs | 8 + compiler/rustc_lint_defs/src/lib.rs | 10 +- compiler/rustc_middle/src/ty/diagnostics.rs | 4 +- compiler/rustc_target/Cargo.toml | 1 + compiler/rustc_target/src/spec/mod.rs | 13 + compiler/rustc_type_ir/Cargo.toml | 2 + compiler/rustc_type_ir/src/ir_print.rs | 49 +++- 23 files changed, 425 insertions(+), 395 deletions(-) create mode 100644 compiler/rustc_error_messages/src/diagnostic_impls.rs (limited to 'compiler/rustc_errors/src') diff --git a/Cargo.lock b/Cargo.lock index 51427f57822..4a72b78f774 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3367,6 +3367,7 @@ dependencies = [ "rand 0.9.2", "rand_xoshiro", "rustc_data_structures", + "rustc_error_messages", "rustc_hashes", "rustc_index", "rustc_macros", @@ -3779,6 +3780,8 @@ dependencies = [ "icu_locid", "icu_provider_adapters", "intl-memoizer", + "rustc_ast", + "rustc_ast_pretty", "rustc_baked_icu_data", "rustc_data_structures", "rustc_macros", @@ -3795,8 +3798,6 @@ dependencies = [ "annotate-snippets 0.11.5", "derive_setters", "rustc_abi", - "rustc_ast", - "rustc_ast_pretty", "rustc_data_structures", "rustc_error_codes", "rustc_error_messages", @@ -3809,8 +3810,6 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", - "rustc_target", - "rustc_type_ir", "serde", "serde_json", "termcolor", @@ -3898,6 +3897,7 @@ dependencies = [ "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", + "rustc_error_messages", "rustc_hashes", "rustc_index", "rustc_macros", @@ -4647,6 +4647,7 @@ dependencies = [ "object 0.37.2", "rustc_abi", "rustc_data_structures", + "rustc_error_messages", "rustc_fs_util", "rustc_macros", "rustc_serialize", @@ -4765,6 +4766,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustc_ast_ir", "rustc_data_structures", + "rustc_error_messages", "rustc_index", "rustc_macros", "rustc_serialize", diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 5f9afc46a1a..83d96d8d04d 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -9,6 +9,7 @@ bitflags = "2.4.1" rand = { version = "0.9.0", default-features = false, optional = true } rand_xoshiro = { version = "0.7.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } +rustc_error_messages = { path = "../rustc_error_messages", optional = true } rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } @@ -24,6 +25,7 @@ default = ["nightly", "randomize"] # without depending on rustc_data_structures, rustc_macros and rustc_serialize nightly = [ "dep:rustc_data_structures", + "dep:rustc_error_messages", "dep:rustc_macros", "dep:rustc_serialize", "dep:rustc_span", diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 29a3678abf3..41d744e1946 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -223,6 +223,9 @@ impl StableOrd for ExternAbi { const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); } +#[cfg(feature = "nightly")] +rustc_error_messages::into_diag_arg_using_display!(ExternAbi); + impl ExternAbi { /// An ABI "like Rust" /// diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 0951859fa53..552ad672752 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -11,6 +11,8 @@ icu_list = "1.2" icu_locid = "1.2" icu_provider_adapters = "1.2" intl-memoizer = "0.5.1" +rustc_ast = { path = "../rustc_ast" } +rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_baked_icu_data = { path = "../rustc_baked_icu_data" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_error_messages/src/diagnostic_impls.rs b/compiler/rustc_error_messages/src/diagnostic_impls.rs new file mode 100644 index 00000000000..3b664cce577 --- /dev/null +++ b/compiler/rustc_error_messages/src/diagnostic_impls.rs @@ -0,0 +1,205 @@ +use std::backtrace::Backtrace; +use std::borrow::Cow; +use std::fmt; +use std::num::ParseIntError; +use std::path::{Path, PathBuf}; +use std::process::ExitStatus; + +use rustc_ast as ast; +use rustc_ast_pretty::pprust; +use rustc_span::edition::Edition; + +use crate::{DiagArgValue, IntoDiagArg}; + +pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display); + +impl IntoDiagArg for DiagArgFromDisplay<'_> { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.0.to_string().into_diag_arg(path) + } +} + +impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> { + fn from(t: &'a dyn fmt::Display) -> Self { + DiagArgFromDisplay(t) + } +} + +impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> { + fn from(t: &'a T) -> Self { + DiagArgFromDisplay(t) + } +} + +impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.clone().into_diag_arg(path) + } +} + +#[macro_export] +macro_rules! into_diag_arg_using_display { + ($( $ty:ty ),+ $(,)?) => { + $( + impl $crate::IntoDiagArg for $ty { + fn into_diag_arg(self, path: &mut Option) -> $crate::DiagArgValue { + self.to_string().into_diag_arg(path) + } + } + )+ + } +} + +macro_rules! into_diag_arg_for_number { + ($( $ty:ty ),+ $(,)?) => { + $( + impl $crate::IntoDiagArg for $ty { + fn into_diag_arg(self, path: &mut Option) -> $crate::DiagArgValue { + // Convert to a string if it won't fit into `Number`. + #[allow(irrefutable_let_patterns)] + if let Ok(n) = TryInto::::try_into(self) { + $crate::DiagArgValue::Number(n) + } else { + self.to_string().into_diag_arg(path) + } + } + } + )+ + } +} + +into_diag_arg_using_display!( + ast::ParamKindOrd, + std::io::Error, + Box, + std::num::NonZero, + Edition, + rustc_span::Ident, + rustc_span::MacroRulesNormalizedIdent, + ParseIntError, + ExitStatus, +); + +into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); + +impl IntoDiagArg for bool { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + if self { + DiagArgValue::Str(Cow::Borrowed("true")) + } else { + DiagArgValue::Str(Cow::Borrowed("false")) + } + } +} + +impl IntoDiagArg for char { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) + } +} + +impl IntoDiagArg for Vec { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::StrListSepByAnd( + self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(), + ) + } +} + +impl IntoDiagArg for rustc_span::Symbol { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_ident_string().into_diag_arg(path) + } +} + +impl<'a> IntoDiagArg for &'a str { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_string().into_diag_arg(path) + } +} + +impl IntoDiagArg for String { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self)) + } +} + +impl<'a> IntoDiagArg for Cow<'a, str> { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.into_owned())) + } +} + +impl<'a> IntoDiagArg for &'a Path { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagArg for PathBuf { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagArg for ast::Expr { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) + } +} + +impl IntoDiagArg for ast::Path { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) + } +} + +impl IntoDiagArg for ast::token::Token { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(pprust::token_to_string(&self)) + } +} + +impl IntoDiagArg for ast::token::TokenKind { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(pprust::token_kind_to_string(&self)) + } +} + +impl IntoDiagArg for std::ffi::CString { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) + } +} + +impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) + } +} + +impl IntoDiagArg for ast::Visibility { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + let s = pprust::vis_to_string(&self); + let s = s.trim_end().to_string(); + DiagArgValue::Str(Cow::Owned(s)) + } +} + +impl IntoDiagArg for Backtrace { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagArg for ast::util::parser::ExprPrecedence { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Number(self as i32) + } +} + +impl IntoDiagArg for ast::FloatTy { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.name_str())) + } +} diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 4b3ecad307f..d8bacbe762b 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -23,6 +23,9 @@ use rustc_span::Span; use tracing::{instrument, trace}; pub use unic_langid::{LanguageIdentifier, langid}; +mod diagnostic_impls; +pub use diagnostic_impls::DiagArgFromDisplay; + pub type FluentBundle = IntoDynSyncSend>; @@ -589,3 +592,53 @@ pub fn fluent_value_from_str_list_sep_by_and(l: Vec>) -> FluentValu FluentValue::Custom(Box::new(FluentStrListSepByAnd(l))) } + +/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of +/// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic +/// emission. +pub type DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue); + +/// Name of a diagnostic argument. +pub type DiagArgName = Cow<'static, str>; + +/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted +/// to a `FluentValue` by the emitter to be used in diagnostic translation. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum DiagArgValue { + Str(Cow<'static, str>), + // This gets converted to a `FluentNumber`, which is an `f64`. An `i32` + // safely fits in an `f64`. Any integers bigger than that will be converted + // to strings in `into_diag_arg` and stored using the `Str` variant. + Number(i32), + StrListSepByAnd(Vec>), +} + +/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct). +/// Implemented as a custom trait rather than `From` so that it is implemented on the type being +/// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to +/// implement this. +pub trait IntoDiagArg { + /// Convert `Self` into a `DiagArgValue` suitable for rendering in a diagnostic. + /// + /// It takes a `path` where "long values" could be written to, if the `DiagArgValue` is too big + /// for displaying on the terminal. This path comes from the `Diag` itself. When rendering + /// values that come from `TyCtxt`, like `Ty<'_>`, they can use `TyCtxt::short_string`. If a + /// value has no shortening logic that could be used, the argument can be safely ignored. + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue; +} + +impl IntoDiagArg for DiagArgValue { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + self + } +} + +impl From for FluentValue<'static> { + fn from(val: DiagArgValue) -> Self { + match val { + DiagArgValue::Str(s) => From::from(s), + DiagArgValue::Number(n) => From::from(n), + DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l), + } + } +} diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 3e8cf6207ae..7fb3533f109 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -8,8 +8,6 @@ edition = "2024" annotate-snippets = "0.11" derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } @@ -22,8 +20,6 @@ rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } -rustc_type_ir = { path = "../rustc_type_ir" } serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.59" termcolor = "1.2.0" diff --git a/compiler/rustc_errors/src/codes.rs b/compiler/rustc_errors/src/codes.rs index 947cf27ca79..787a8af99b1 100644 --- a/compiler/rustc_errors/src/codes.rs +++ b/compiler/rustc_errors/src/codes.rs @@ -20,6 +20,8 @@ impl fmt::Display for ErrCode { } } +rustc_error_messages::into_diag_arg_using_display!(ErrCode); + macro_rules! define_error_code_constants_and_diagnostics_table { ($($name:ident: $num:literal,)*) => ( $( diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index e579370ce4e..183dceddd2c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use std::thread::panicking; use rustc_data_structures::fx::FxIndexMap; -use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and}; +use rustc_error_messages::{DiagArgName, DiagArgValue, IntoDiagArg}; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; @@ -22,26 +22,6 @@ use crate::{ Suggestions, }; -/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of -/// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic -/// emission. -pub type DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue); - -/// Name of a diagnostic argument. -pub type DiagArgName = Cow<'static, str>; - -/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted -/// to a `FluentValue` by the emitter to be used in diagnostic translation. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] -pub enum DiagArgValue { - Str(Cow<'static, str>), - // This gets converted to a `FluentNumber`, which is an `f64`. An `i32` - // safely fits in an `f64`. Any integers bigger than that will be converted - // to strings in `into_diag_arg` and stored using the `Str` variant. - Number(i32), - StrListSepByAnd(Vec>), -} - pub type DiagArgMap = FxIndexMap; /// Trait for types that `Diag::emit` can return as a "guarantee" (or "proof") @@ -143,36 +123,6 @@ where } } -/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct). -/// Implemented as a custom trait rather than `From` so that it is implemented on the type being -/// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to -/// implement this. -pub trait IntoDiagArg { - /// Convert `Self` into a `DiagArgValue` suitable for rendering in a diagnostic. - /// - /// It takes a `path` where "long values" could be written to, if the `DiagArgValue` is too big - /// for displaying on the terminal. This path comes from the `Diag` itself. When rendering - /// values that come from `TyCtxt`, like `Ty<'_>`, they can use `TyCtxt::short_string`. If a - /// value has no shortening logic that could be used, the argument can be safely ignored. - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue; -} - -impl IntoDiagArg for DiagArgValue { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - self - } -} - -impl From for FluentValue<'static> { - fn from(val: DiagArgValue) -> Self { - match val { - DiagArgValue::Str(s) => From::from(s), - DiagArgValue::Number(n) => From::from(n), - DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l), - } - } -} - /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[rustc_diagnostic_item = "Subdiagnostic"] diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 698b6b8d504..435d16a8380 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,340 +1,22 @@ -use std::backtrace::Backtrace; use std::borrow::Cow; -use std::fmt; -use std::num::ParseIntError; -use std::path::{Path, PathBuf}; -use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_ast_pretty::pprust; -use rustc_hir::RustcVersion; -use rustc_hir::attrs::{MirDialect, MirPhase}; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::Subdiagnostic; -use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; -use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTuple}; -use rustc_type_ir::{ClosureKind, FloatTy}; -use {rustc_ast as ast, rustc_hir as hir}; +use rustc_span::{Span, Symbol}; use crate::diagnostic::DiagLocation; use crate::{ - Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level, - Subdiagnostic, fluent_generated as fluent, + Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, + fluent_generated as fluent, }; -pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display); - -impl IntoDiagArg for DiagArgFromDisplay<'_> { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.0.to_string().into_diag_arg(path) - } -} - -impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> { - fn from(t: &'a dyn fmt::Display) -> Self { - DiagArgFromDisplay(t) - } -} - -impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> { - fn from(t: &'a T) -> Self { - DiagArgFromDisplay(t) - } -} - -impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.clone().into_diag_arg(path) - } -} - -#[macro_export] -macro_rules! into_diag_arg_using_display { - ($( $ty:ty ),+ $(,)?) => { - $( - impl IntoDiagArg for $ty { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } - } - )+ - } -} - -macro_rules! into_diag_arg_for_number { - ($( $ty:ty ),+ $(,)?) => { - $( - impl IntoDiagArg for $ty { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - // Convert to a string if it won't fit into `Number`. - #[allow(irrefutable_let_patterns)] - if let Ok(n) = TryInto::::try_into(self) { - DiagArgValue::Number(n) - } else { - self.to_string().into_diag_arg(path) - } - } - } - )+ - } -} - -into_diag_arg_using_display!( - ast::ParamKindOrd, - std::io::Error, - Box, - std::num::NonZero, - hir::Target, - Edition, - Ident, - MacroRulesNormalizedIdent, - ParseIntError, - StackProtector, - &TargetTuple, - SplitDebuginfo, - ExitStatus, - ErrCode, - rustc_abi::ExternAbi, -); - -impl IntoDiagArg for RustcVersion { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagArg for rustc_type_ir::TraitRef { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::ExistentialTraitRef { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::UnevaluatedConst { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - format!("{self:?}").into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::FnSig { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - format!("{self:?}").into_diag_arg(path) - } -} - -impl IntoDiagArg for rustc_type_ir::Binder -where - T: IntoDiagArg, -{ - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.skip_binder().into_diag_arg(path) - } -} - -into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); - -impl IntoDiagArg for bool { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - if self { - DiagArgValue::Str(Cow::Borrowed("true")) - } else { - DiagArgValue::Str(Cow::Borrowed("false")) - } - } -} - -impl IntoDiagArg for char { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) - } -} - -impl IntoDiagArg for Vec { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::StrListSepByAnd( - self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(), - ) - } -} - -impl IntoDiagArg for Symbol { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_ident_string().into_diag_arg(path) - } -} - -impl<'a> IntoDiagArg for &'a str { - fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { - self.to_string().into_diag_arg(path) - } -} - -impl IntoDiagArg for String { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self)) - } -} - -impl<'a> IntoDiagArg for Cow<'a, str> { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.into_owned())) - } -} - -impl<'a> IntoDiagArg for &'a Path { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.display().to_string())) - } -} - -impl IntoDiagArg for PathBuf { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.display().to_string())) - } -} - -impl IntoDiagArg for PanicStrategy { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.desc().to_string())) - } -} - -impl IntoDiagArg for hir::ConstContext { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(match self { - hir::ConstContext::ConstFn => "const_fn", - hir::ConstContext::Static(_) => "static", - hir::ConstContext::Const { .. } => "const", - })) - } -} - -impl IntoDiagArg for ast::Expr { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) - } -} - -impl IntoDiagArg for ast::Path { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) - } -} - -impl IntoDiagArg for ast::token::Token { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(pprust::token_to_string(&self)) - } -} - -impl IntoDiagArg for ast::token::TokenKind { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(pprust::token_kind_to_string(&self)) - } -} - -impl IntoDiagArg for FloatTy { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.name_str())) - } -} - -impl IntoDiagArg for std::ffi::CString { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) - } -} - -impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) - } -} - -impl IntoDiagArg for ast::Visibility { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - let s = pprust::vis_to_string(&self); - let s = s.trim_end().to_string(); - DiagArgValue::Str(Cow::Owned(s)) - } -} - -impl IntoDiagArg for rustc_lint_defs::Level { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) - } -} - -impl IntoDiagArg for hir::def::Res { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.descr())) - } -} - impl IntoDiagArg for DiagLocation { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::from(self.to_string())) } } -impl IntoDiagArg for Backtrace { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - -impl IntoDiagArg for Level { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - -impl IntoDiagArg for ClosureKind { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(self.as_str().into()) - } -} - -impl IntoDiagArg for hir::def::Namespace { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.descr())) - } -} - -impl IntoDiagArg for ExprPrecedence { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Number(self as i32) - } -} - -impl IntoDiagArg for MirDialect { - fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { - let arg = match self { - MirDialect::Analysis => "analysis", - MirDialect::Built => "built", - MirDialect::Runtime => "runtime", - }; - DiagArgValue::Str(Cow::Borrowed(arg)) - } -} - -impl IntoDiagArg for MirPhase { - fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { - let arg = match self { - MirPhase::Initial => "initial", - MirPhase::PostCleanup => "post-cleanup", - MirPhase::Optimized => "optimized", - }; - DiagArgValue::Str(Cow::Borrowed(arg)) - } -} - #[derive(Clone)] pub struct DiagSymbolList(Vec); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2534cddf105..0a2bfee8b29 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -41,12 +41,11 @@ use std::{fmt, panic}; use Level::*; pub use codes::*; pub use diagnostic::{ - BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString, - Diagnostic, EmissionGuarantee, FatalAbort, IntoDiagArg, LintDiagnostic, StringPart, Subdiag, - Subdiagnostic, + BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, + FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ - DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, + DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; @@ -56,8 +55,9 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{DynSend, Lock}; pub use rustc_error_messages::{ - DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, - SubdiagMessage, fallback_fluent_bundle, fluent_bundle, + DiagArg, DiagArgFromDisplay, DiagArgName, DiagArgValue, DiagMessage, FluentBundle, IntoDiagArg, + LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage, + fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; use rustc_hir::HirId; @@ -1999,6 +1999,12 @@ impl Level { } } +impl IntoDiagArg for Level { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. pub fn elided_lifetime_in_path_suggestion( source_map: &SourceMap, diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 71496b7ec32..592a9996c8f 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -12,6 +12,7 @@ rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_error_messages = { path = "../rustc_error_messages" } rustc_hashes = { path = "../rustc_hashes" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 31715955ed3..a17350f0392 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1,7 +1,11 @@ +use std::borrow::Cow; +use std::path::PathBuf; + pub use ReprAttr::*; use rustc_abi::Align; use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, ast}; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; @@ -213,6 +217,17 @@ pub enum MirDialect { Runtime, } +impl IntoDiagArg for MirDialect { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let arg = match self { + MirDialect::Analysis => "analysis", + MirDialect::Built => "built", + MirDialect::Runtime => "runtime", + }; + DiagArgValue::Str(Cow::Borrowed(arg)) + } +} + #[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)] #[derive(HashStable_Generic, PrintAttribute)] pub enum MirPhase { @@ -221,6 +236,17 @@ pub enum MirPhase { Optimized, } +impl IntoDiagArg for MirPhase { + fn into_diag_arg(self, _path: &mut Option) -> DiagArgValue { + let arg = match self { + MirPhase::Initial => "initial", + MirPhase::PostCleanup => "post-cleanup", + MirPhase::Optimized => "optimized", + }; + DiagArgValue::Str(Cow::Borrowed(arg)) + } +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 79319e24266..8af4740f376 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -1,10 +1,12 @@ use std::array::IntoIter; +use std::borrow::Cow; use std::fmt::Debug; use rustc_ast as ast; use rustc_ast::NodeId; use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::Symbol; use rustc_span::def_id::{DefId, LocalDefId}; @@ -586,6 +588,12 @@ pub enum Res { Err, } +impl IntoDiagArg for Res { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + /// The result of resolving a path before lowering to HIR, /// with "module" segments resolved and associated item /// segments deferred to type checking. @@ -673,6 +681,12 @@ impl Namespace { } } +impl IntoDiagArg for Namespace { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + impl ToStableHashKey for Namespace { type KeyType = Namespace; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2c8986b7c7d..39696f74d51 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,4 +1,5 @@ // ignore-tidy-filelength +use std::borrow::Cow; use std::fmt; use rustc_abi::ExternAbi; @@ -17,6 +18,7 @@ pub use rustc_ast::{ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::tagged_ptr::TaggedRef; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; @@ -2259,8 +2261,15 @@ impl fmt::Display for ConstContext { } } -// NOTE: `IntoDiagArg` impl for `ConstContext` lives in `rustc_errors` -// due to a cyclical dependency between hir and that crate. +impl IntoDiagArg for ConstContext { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(match self { + ConstContext::ConstFn => "const_fn", + ConstContext::Static(_) => "static", + ConstContext::Const { .. } => "const", + })) + } +} /// A literal. pub type Lit = Spanned; diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index f68dad3a5e8..dcac51b10b4 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -79,6 +79,8 @@ impl Display for Target { } } +rustc_error_messages::into_diag_arg_using_display!(Target); + impl Target { pub fn is_associated_item(self) -> bool { match self { diff --git a/compiler/rustc_hir/src/version.rs b/compiler/rustc_hir/src/version.rs index ab5ab026b4c..bc2c38a4935 100644 --- a/compiler/rustc_hir/src/version.rs +++ b/compiler/rustc_hir/src/version.rs @@ -1,6 +1,8 @@ +use std::borrow::Cow; use std::fmt::{self, Display}; use std::sync::OnceLock; +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{ Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version, }; @@ -45,3 +47,9 @@ impl Display for RustcVersion { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } + +impl IntoDiagArg for RustcVersion { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string())) + } +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 3bb7bbce567..94a290fa5b2 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use rustc_abi::ExternAbi; use rustc_ast::AttrId; use rustc_ast::attr::AttributeExt; @@ -6,7 +8,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; -use rustc_error_messages::{DiagMessage, MultiSpan}; +use rustc_error_messages::{DiagArgValue, DiagMessage, IntoDiagArg, MultiSpan}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefPathHash; use rustc_hir::{HashStableContext, HirId, ItemLocalId}; @@ -297,6 +299,12 @@ impl Level { } } +impl IntoDiagArg for Level { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) + } +} + /// Specification of a single lint. #[derive(Copy, Clone, Debug)] pub struct Lint { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index b3042904a29..b9a6f67ab0d 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -23,7 +23,7 @@ impl IntoDiagArg for Ty<'_> { fn into_diag_arg(self, path: &mut Option) -> rustc_errors::DiagArgValue { ty::tls::with(|tcx| { let ty = tcx.short_string(self, path); - rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(ty)) + DiagArgValue::Str(std::borrow::Cow::Owned(ty)) }) } } @@ -32,7 +32,7 @@ impl IntoDiagArg for Instance<'_> { fn into_diag_arg(self, path: &mut Option) -> rustc_errors::DiagArgValue { ty::tls::with(|tcx| { let instance = tcx.short_string_namespace(self, path, Namespace::ValueNS); - rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(instance)) + DiagArgValue::Str(std::borrow::Cow::Owned(instance)) }) } } diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index 56932c24922..57c90a703f1 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" bitflags = "2.4.1" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_error_messages = { path = "../rustc_error_messages" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 2aa8ab5d317..399770022b2 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -50,6 +50,7 @@ use rustc_abi::{ Align, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors, }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_error_messages::{DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -875,6 +876,12 @@ impl FromStr for PanicStrategy { crate::json::serde_deserialize_from_str!(PanicStrategy); +impl IntoDiagArg for PanicStrategy { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.desc().to_string())) + } +} + impl ToJson for PanicStrategy { fn to_json(&self) -> Json { match *self { @@ -1518,6 +1525,8 @@ impl fmt::Display for SplitDebuginfo { } } +into_diag_arg_using_display!(SplitDebuginfo); + #[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)] #[serde(tag = "kind")] #[serde(rename_all = "kebab-case")] @@ -1795,6 +1804,8 @@ impl fmt::Display for StackProtector { } } +into_diag_arg_using_display!(StackProtector); + #[derive(PartialEq, Clone, Debug)] pub enum BinaryFormat { Coff, @@ -3806,3 +3817,5 @@ impl fmt::Display for TargetTuple { write!(f, "{}", self.debug_tuple()) } } + +into_diag_arg_using_display!(&TargetTuple); diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index e7fee574dd4..d55e9b3b1be 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -12,6 +12,7 @@ indexmap = "2.0.0" rustc-hash = "2.0.0" rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false } rustc_data_structures = { path = "../rustc_data_structures", optional = true } +rustc_error_messages = { path = "../rustc_error_messages", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } @@ -27,6 +28,7 @@ tracing = "0.1" default = ["nightly"] nightly = [ "dep:rustc_data_structures", + "dep:rustc_error_messages", "dep:rustc_macros", "dep:rustc_serialize", "dep:rustc_span", diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 388ad09cb20..82bb8791b84 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,9 +1,9 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, - ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, + AliasTerm, AliasTy, Binder, ClosureKind, CoercePredicate, ExistentialProjection, + ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, + PatternKind, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, UnevaluatedConst, }; pub trait IrPrint { @@ -70,3 +70,46 @@ where >>::print(self, fmt) } } + +#[cfg(feature = "nightly")] +mod into_diag_arg_impls { + use rustc_error_messages::{DiagArgValue, IntoDiagArg}; + + use super::*; + + impl IntoDiagArg for TraitRef { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_string().into_diag_arg(path) + } + } + + impl IntoDiagArg for ExistentialTraitRef { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.to_string().into_diag_arg(path) + } + } + + impl IntoDiagArg for UnevaluatedConst { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + format!("{self:?}").into_diag_arg(path) + } + } + + impl IntoDiagArg for FnSig { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + format!("{self:?}").into_diag_arg(path) + } + } + + impl IntoDiagArg for Binder { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + self.skip_binder().into_diag_arg(path) + } + } + + impl IntoDiagArg for ClosureKind { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + DiagArgValue::Str(self.as_str().into()) + } + } +} -- cgit 1.4.1-3-g733a5 From f3c8b7ad40c405989be968dc170e487d360fd6da Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 14 Aug 2025 01:33:59 -0700 Subject: Split `rustc_hir_id` out of `rustc_hir` Some crates depend on `rustc_hir` but only want `HirId` and similar id types. `rustc_hir` is a heavy dependency, since it pulls in `rustc_target`. Split these types out into their own crate `rustc_hir_id`. This allows `rustc_errors` to drop its direct dependency on `rustc_hir`. (`rustc_errors` still depends on `rustc_hir` indirectly through `rustc_lint_defs`; a subsequent commit will fix that.) --- Cargo.lock | 15 ++- compiler/rustc_errors/Cargo.toml | 2 +- compiler/rustc_errors/src/lib.rs | 2 +- compiler/rustc_hir/Cargo.toml | 1 + compiler/rustc_hir/src/hir_id.rs | 173 ------------------------- compiler/rustc_hir/src/lib.rs | 6 +- compiler/rustc_hir/src/stable_hash_impls.rs | 21 +-- compiler/rustc_hir_id/Cargo.toml | 13 ++ compiler/rustc_hir_id/src/lib.rs | 194 ++++++++++++++++++++++++++++ compiler/rustc_lint_defs/Cargo.toml | 1 + compiler/rustc_lint_defs/src/lib.rs | 6 +- 11 files changed, 230 insertions(+), 204 deletions(-) delete mode 100644 compiler/rustc_hir/src/hir_id.rs create mode 100644 compiler/rustc_hir_id/Cargo.toml create mode 100644 compiler/rustc_hir_id/src/lib.rs (limited to 'compiler/rustc_errors/src') diff --git a/Cargo.lock b/Cargo.lock index 4a72b78f774..a0d1b250abc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3803,7 +3803,7 @@ dependencies = [ "rustc_error_messages", "rustc_fluent_macro", "rustc_hashes", - "rustc_hir", + "rustc_hir_id", "rustc_index", "rustc_lexer", "rustc_lint_defs", @@ -3899,6 +3899,7 @@ dependencies = [ "rustc_data_structures", "rustc_error_messages", "rustc_hashes", + "rustc_hir_id", "rustc_index", "rustc_macros", "rustc_serialize", @@ -3936,6 +3937,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_hir_id" +version = "0.0.0" +dependencies = [ + "rustc_data_structures", + "rustc_index", + "rustc_macros", + "rustc_serialize", + "rustc_span", +] + [[package]] name = "rustc_hir_pretty" version = "0.0.0" @@ -4128,6 +4140,7 @@ dependencies = [ "rustc_data_structures", "rustc_error_messages", "rustc_hir", + "rustc_hir_id", "rustc_macros", "rustc_serialize", "rustc_span", diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 7fb3533f109..ad6d29e21fc 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -13,7 +13,7 @@ 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 = { path = "../rustc_hir" } +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 0a2bfee8b29..a775b70dbee 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -60,7 +60,7 @@ pub use rustc_error_messages::{ fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display, }; use rustc_hashes::Hash128; -use rustc_hir::HirId; +use rustc_hir_id::HirId; pub use rustc_lint_defs::{Applicability, listify, pluralize}; use rustc_lint_defs::{Lint, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 592a9996c8f..1008a3e787d 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -14,6 +14,7 @@ rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_hashes = { path = "../rustc_hashes" } +rustc_hir_id = { path = "../rustc_hir_id" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs deleted file mode 100644 index b48a081d371..00000000000 --- a/compiler/rustc_hir/src/hir_id.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::fmt::{self, Debug}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; -use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::HashStableContext; -use rustc_span::def_id::DefPathHash; - -use crate::def_id::{CRATE_DEF_ID, DefId, DefIndex, LocalDefId}; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] -pub struct OwnerId { - pub def_id: LocalDefId, -} - -impl Debug for OwnerId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Example: DefId(0:1 ~ aa[7697]::{use#0}) - Debug::fmt(&self.def_id, f) - } -} - -impl From for HirId { - fn from(owner: OwnerId) -> HirId { - HirId { owner, local_id: ItemLocalId::ZERO } - } -} - -impl From for DefId { - fn from(value: OwnerId) -> Self { - value.to_def_id() - } -} - -impl OwnerId { - #[inline] - pub fn to_def_id(self) -> DefId { - self.def_id.to_def_id() - } -} - -impl rustc_index::Idx for OwnerId { - #[inline] - fn new(idx: usize) -> Self { - OwnerId { def_id: LocalDefId { local_def_index: DefIndex::from_usize(idx) } } - } - - #[inline] - fn index(self) -> usize { - self.def_id.local_def_index.as_usize() - } -} - -impl HashStable for OwnerId { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - self.to_stable_hash_key(hcx).hash_stable(hcx, hasher); - } -} - -impl ToStableHashKey for OwnerId { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &CTX) -> DefPathHash { - hcx.def_path_hash(self.to_def_id()) - } -} - -/// Uniquely identifies a node in the HIR of the current crate. It is -/// composed of the `owner`, which is the `LocalDefId` of the directly enclosing -/// `hir::Item`, `hir::TraitItem`, or `hir::ImplItem` (i.e., the closest "item-like"), -/// and the `local_id` which is unique within the given owner. -/// -/// This two-level structure makes for more stable values: One can move an item -/// around within the source code, or add or remove stuff before it, without -/// the `local_id` part of the `HirId` changing, which is a very useful property in -/// incremental compilation where we have to persist things through changes to -/// the code base. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] -#[rustc_pass_by_value] -pub struct HirId { - pub owner: OwnerId, - pub local_id: ItemLocalId, -} - -// To ensure correctness of incremental compilation, -// `HirId` must not implement `Ord` or `PartialOrd`. -// See https://github.com/rust-lang/rust/issues/90317. -impl !Ord for HirId {} -impl !PartialOrd for HirId {} - -impl Debug for HirId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10) - // Don't use debug_tuple to always keep this on one line. - write!(f, "HirId({:?}.{:?})", self.owner, self.local_id) - } -} - -impl HirId { - /// Signal local id which should never be used. - pub const INVALID: HirId = - HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::INVALID }; - - #[inline] - pub fn expect_owner(self) -> OwnerId { - assert_eq!(self.local_id.index(), 0); - self.owner - } - - #[inline] - pub fn as_owner(self) -> Option { - if self.local_id.index() == 0 { Some(self.owner) } else { None } - } - - #[inline] - pub fn is_owner(self) -> bool { - self.local_id.index() == 0 - } - - #[inline] - pub fn make_owner(owner: LocalDefId) -> Self { - Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO } - } -} - -impl fmt::Display for HirId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") - } -} - -rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId); -rustc_data_structures::define_id_collections!( - ItemLocalMap, - ItemLocalSet, - ItemLocalMapEntry, - ItemLocalId -); - -rustc_index::newtype_index! { - /// An `ItemLocalId` uniquely identifies something within a given "item-like"; - /// that is, within a `hir::Item`, `hir::TraitItem`, or `hir::ImplItem`. There is no - /// guarantee that the numerical value of a given `ItemLocalId` corresponds to - /// the node's position within the owning item in any way, but there is a - /// guarantee that the `ItemLocalId`s within an owner occupy a dense range of - /// integers starting at zero, so a mapping that maps all or most nodes within - /// an "item-like" to something else can be implemented by a `Vec` instead of a - /// tree or hash map. - #[derive(HashStable_Generic)] - #[encodable] - #[orderable] - pub struct ItemLocalId {} -} - -impl ItemLocalId { - /// Signal local id which should never be used. - pub const INVALID: ItemLocalId = ItemLocalId::MAX; -} - -impl StableOrd for ItemLocalId { - const CAN_USE_UNSTABLE_SORT: bool = true; - - // `Ord` is implemented as just comparing the ItemLocalId's numerical - // values and these are not changed by (de-)serialization. - const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); -} - -/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. -pub const CRATE_HIR_ID: HirId = - HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; - -pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index f1212d07ff6..78fc63753a2 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -3,14 +3,11 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html // tidy-alphabetical-start -#![allow(internal_features)] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(debug_closure_helpers)] #![feature(exhaustive_patterns)] -#![feature(negative_impls)] #![feature(never_type)] -#![feature(rustc_attrs)] #![feature(variant_count)] #![recursion_limit = "256"] // tidy-alphabetical-end @@ -25,7 +22,7 @@ pub mod definitions; pub mod diagnostic_items; pub use rustc_span::def_id; mod hir; -pub mod hir_id; +pub use rustc_hir_id::{self as hir_id, *}; pub mod intravisit; pub mod lang_items; pub mod lints; @@ -41,7 +38,6 @@ mod tests; #[doc(no_inline)] pub use hir::*; -pub use hir_id::*; pub use lang_items::{LangItem, LanguageItems}; pub use stability::*; pub use stable_hash_impls::HashStableContext; diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index ecc608d437b..16e8bac3d8a 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -5,7 +5,7 @@ use crate::HashIgnoredAttrId; use crate::hir::{ AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; -use crate::hir_id::{HirId, ItemLocalId}; +use crate::hir_id::ItemLocalId; use crate::lints::DelayedLints; /// Requirements for a `StableHashingContext` to be used in this crate. @@ -15,25 +15,6 @@ pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStabl fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher); } -impl ToStableHashKey for HirId { - type KeyType = (DefPathHash, ItemLocalId); - - #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> (DefPathHash, ItemLocalId) { - let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); - (def_path_hash, self.local_id) - } -} - -impl ToStableHashKey for ItemLocalId { - type KeyType = ItemLocalId; - - #[inline] - fn to_stable_hash_key(&self, _: &HirCtx) -> ItemLocalId { - *self - } -} - impl ToStableHashKey for BodyId { type KeyType = (DefPathHash, ItemLocalId); diff --git a/compiler/rustc_hir_id/Cargo.toml b/compiler/rustc_hir_id/Cargo.toml new file mode 100644 index 00000000000..c357a4f62d9 --- /dev/null +++ b/compiler/rustc_hir_id/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rustc_hir_id" +version = "0.0.0" +edition = "2024" + +[dependencies] +# tidy-alphabetical-start +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_index = { path = "../rustc_index" } +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } +# tidy-alphabetical-end diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs new file mode 100644 index 00000000000..17c3e16b467 --- /dev/null +++ b/compiler/rustc_hir_id/src/lib.rs @@ -0,0 +1,194 @@ +#![allow(internal_features)] +#![feature(negative_impls)] +#![feature(rustc_attrs)] + +use std::fmt::{self, Debug}; + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +pub use rustc_span::HashStableContext; +use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] +pub struct OwnerId { + pub def_id: LocalDefId, +} + +impl Debug for OwnerId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Example: DefId(0:1 ~ aa[7697]::{use#0}) + Debug::fmt(&self.def_id, f) + } +} + +impl From for HirId { + fn from(owner: OwnerId) -> HirId { + HirId { owner, local_id: ItemLocalId::ZERO } + } +} + +impl From for DefId { + fn from(value: OwnerId) -> Self { + value.to_def_id() + } +} + +impl OwnerId { + #[inline] + pub fn to_def_id(self) -> DefId { + self.def_id.to_def_id() + } +} + +impl rustc_index::Idx for OwnerId { + #[inline] + fn new(idx: usize) -> Self { + OwnerId { def_id: LocalDefId { local_def_index: DefIndex::from_usize(idx) } } + } + + #[inline] + fn index(self) -> usize { + self.def_id.local_def_index.as_usize() + } +} + +impl HashStable for OwnerId { + #[inline] + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.to_stable_hash_key(hcx).hash_stable(hcx, hasher); + } +} + +impl ToStableHashKey for OwnerId { + type KeyType = DefPathHash; + + #[inline] + fn to_stable_hash_key(&self, hcx: &CTX) -> DefPathHash { + hcx.def_path_hash(self.to_def_id()) + } +} + +/// Uniquely identifies a node in the HIR of the current crate. It is +/// composed of the `owner`, which is the `LocalDefId` of the directly enclosing +/// `hir::Item`, `hir::TraitItem`, or `hir::ImplItem` (i.e., the closest "item-like"), +/// and the `local_id` which is unique within the given owner. +/// +/// This two-level structure makes for more stable values: One can move an item +/// around within the source code, or add or remove stuff before it, without +/// the `local_id` part of the `HirId` changing, which is a very useful property in +/// incremental compilation where we have to persist things through changes to +/// the code base. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)] +#[rustc_pass_by_value] +pub struct HirId { + pub owner: OwnerId, + pub local_id: ItemLocalId, +} + +// To ensure correctness of incremental compilation, +// `HirId` must not implement `Ord` or `PartialOrd`. +// See https://github.com/rust-lang/rust/issues/90317. +impl !Ord for HirId {} +impl !PartialOrd for HirId {} + +impl Debug for HirId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10) + // Don't use debug_tuple to always keep this on one line. + write!(f, "HirId({:?}.{:?})", self.owner, self.local_id) + } +} + +impl HirId { + /// Signal local id which should never be used. + pub const INVALID: HirId = + HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::INVALID }; + + #[inline] + pub fn expect_owner(self) -> OwnerId { + assert_eq!(self.local_id.index(), 0); + self.owner + } + + #[inline] + pub fn as_owner(self) -> Option { + if self.local_id.index() == 0 { Some(self.owner) } else { None } + } + + #[inline] + pub fn is_owner(self) -> bool { + self.local_id.index() == 0 + } + + #[inline] + pub fn make_owner(owner: LocalDefId) -> Self { + Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO } + } +} + +impl fmt::Display for HirId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } +} + +rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId); +rustc_data_structures::define_id_collections!( + ItemLocalMap, + ItemLocalSet, + ItemLocalMapEntry, + ItemLocalId +); + +rustc_index::newtype_index! { + /// An `ItemLocalId` uniquely identifies something within a given "item-like"; + /// that is, within a `hir::Item`, `hir::TraitItem`, or `hir::ImplItem`. There is no + /// guarantee that the numerical value of a given `ItemLocalId` corresponds to + /// the node's position within the owning item in any way, but there is a + /// guarantee that the `ItemLocalId`s within an owner occupy a dense range of + /// integers starting at zero, so a mapping that maps all or most nodes within + /// an "item-like" to something else can be implemented by a `Vec` instead of a + /// tree or hash map. + #[derive(HashStable_Generic)] + #[encodable] + #[orderable] + pub struct ItemLocalId {} +} + +impl ItemLocalId { + /// Signal local id which should never be used. + pub const INVALID: ItemLocalId = ItemLocalId::MAX; +} + +impl StableOrd for ItemLocalId { + const CAN_USE_UNSTABLE_SORT: bool = true; + + // `Ord` is implemented as just comparing the ItemLocalId's numerical + // values and these are not changed by (de-)serialization. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); +} + +/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. +pub const CRATE_HIR_ID: HirId = + HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; + +pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; + +impl ToStableHashKey for HirId { + type KeyType = (DefPathHash, ItemLocalId); + + #[inline] + fn to_stable_hash_key(&self, hcx: &CTX) -> (DefPathHash, ItemLocalId) { + let def_path_hash = self.owner.def_id.to_stable_hash_key(hcx); + (def_path_hash, self.local_id) + } +} + +impl ToStableHashKey for ItemLocalId { + type KeyType = ItemLocalId; + + #[inline] + fn to_stable_hash_key(&self, _: &CTX) -> ItemLocalId { + *self + } +} diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index 9ab350daf69..a7422d610fc 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -10,6 +10,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_id = { path = "../rustc_hir_id" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 94a290fa5b2..14a95789951 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -11,7 +11,7 @@ use rustc_data_structures::stable_hasher::{ use rustc_error_messages::{DiagArgValue, DiagMessage, IntoDiagArg, MultiSpan}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefPathHash; -use rustc_hir::{HashStableContext, HirId, ItemLocalId}; +use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::edition::Edition; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; @@ -140,7 +140,7 @@ impl LintExpectationId { } } -impl HashStable for LintExpectationId { +impl HashStable for LintExpectationId { #[inline] fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { match self { @@ -158,7 +158,7 @@ impl HashStable for LintExpectationId { } } -impl ToStableHashKey for LintExpectationId { +impl ToStableHashKey for LintExpectationId { type KeyType = (DefPathHash, ItemLocalId, u16, u16); #[inline] -- cgit 1.4.1-3-g733a5 From c99320156dfeea98836fe7dad97d16f67c4d879e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 21 Aug 2025 20:30:28 -0700 Subject: Refactor lint buffering to avoid requiring a giant enum Lint buffering currently relies on a giant enum `BuiltinLintDiag` containing all the lints that might potentially get buffered. In addition to being an unwieldy enum in a central crate, this also makes `rustc_lint_defs` a build bottleneck: it depends on various types from various crates (with a steady pressure to add more), and many crates depend on it. Having all of these variants in a separate crate also prevents detecting when a variant becomes unused, which we can do with a dedicated type defined and used in the same crate. Refactor this to use a dyn trait, to allow using `LintDiagnostic` types directly. This requires boxing, but all of this is already on the slow path (emitting an error). Because the existing `BuiltinLintDiag` requires some additional types in order to decorate some variants, which are only available later in `rustc_lint`, use an enum `DecorateDiagCompat` to handle both the `dyn LintDiagnostic` case and the `BuiltinLintDiag` case. --- Cargo.lock | 1 + compiler/rustc_ast_passes/src/ast_validation.rs | 4 +- compiler/rustc_builtin_macros/src/format.rs | 8 ++- compiler/rustc_errors/Cargo.toml | 1 + compiler/rustc_errors/src/decorate_diag.rs | 85 +++++++++++++++++++++++++ compiler/rustc_errors/src/diagnostic.rs | 12 +++- compiler/rustc_errors/src/lib.rs | 4 +- compiler/rustc_expand/src/base.rs | 4 +- compiler/rustc_interface/src/util.rs | 3 +- compiler/rustc_lint/src/context.rs | 4 +- compiler/rustc_lint/src/early.rs | 10 ++- compiler/rustc_lint/src/lib.rs | 5 +- compiler/rustc_lint_defs/src/lib.rs | 52 +-------------- compiler/rustc_middle/src/middle/stability.rs | 4 +- compiler/rustc_middle/src/ty/mod.rs | 3 +- compiler/rustc_resolve/src/lib.rs | 4 +- compiler/rustc_session/src/config/cfg.rs | 2 +- compiler/rustc_session/src/parse.rs | 14 ++-- 18 files changed, 137 insertions(+), 83 deletions(-) create mode 100644 compiler/rustc_errors/src/decorate_diag.rs (limited to 'compiler/rustc_errors/src') diff --git a/Cargo.lock b/Cargo.lock index 6f7a309894c..01f012d5edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3798,6 +3798,7 @@ dependencies = [ "annotate-snippets 0.11.5", "derive_setters", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_error_codes", "rustc_error_messages", diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ef4410566c5..4bcb126dfc2 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -26,15 +26,15 @@ use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list} use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, LintBuffer}; use rustc_feature::Features; use rustc_parse::validate_attr; use rustc_session::Session; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, }; -use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index ec613b7b710..6415e55e0b0 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -10,11 +10,12 @@ use rustc_ast::{ }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize, + Applicability, BufferedEarlyLint, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, + pluralize, }; use rustc_expand::base::*; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; -use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; +use rustc_lint_defs::{BuiltinLintDiag, LintId}; use rustc_parse::exp; use rustc_parse_format as parse; use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol}; @@ -595,7 +596,8 @@ fn make_format_args( named_arg_sp: arg_name.span, named_arg_name: arg_name.name.to_string(), is_formatting_arg: matches!(used_as, Width | Precision), - }, + } + .into(), }); } } diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index ad6d29e21fc..f37b6fb748f 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" annotate-snippets = "0.11" derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs new file mode 100644 index 00000000000..5aef26ccf97 --- /dev/null +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -0,0 +1,85 @@ +/// This module provides types and traits for buffering lints until later in compilation. +use rustc_ast::node_id::NodeId; +use rustc_data_structures::fx::FxIndexMap; +use rustc_error_messages::MultiSpan; +use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId}; + +use crate::{DynSend, LintDiagnostic, LintDiagnosticBox}; + +/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its +/// variants requires types we don't have yet. So, handle that case separately. +pub enum DecorateDiagCompat { + Dynamic(Box LintDiagnosticBox<'a, ()> + DynSend + 'static>), + Builtin(BuiltinLintDiag), +} + +impl std::fmt::Debug for DecorateDiagCompat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DecorateDiagCompat").finish() + } +} + +impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {} + +impl LintDiagnostic<'a, ()> + DynSend + 'static> From for DecorateDiagCompat { + #[inline] + fn from(d: D) -> Self { + Self::Dynamic(Box::new(d)) + } +} + +impl From for DecorateDiagCompat { + #[inline] + fn from(b: BuiltinLintDiag) -> Self { + Self::Builtin(b) + } +} + +/// Lints that are buffered up early on in the `Session` before the +/// `LintLevels` is calculated. +#[derive(Debug)] +pub struct BufferedEarlyLint { + /// The span of code that we are linting on. + pub span: Option, + + /// The `NodeId` of the AST node that generated the lint. + pub node_id: NodeId, + + /// A lint Id that can be passed to + /// `rustc_lint::early::EarlyContextAndPass::check_id`. + pub lint_id: LintId, + + /// Customization of the `Diag<'_>` for the lint. + pub diagnostic: DecorateDiagCompat, +} + +#[derive(Default, Debug)] +pub struct LintBuffer { + pub map: FxIndexMap>, +} + +impl LintBuffer { + pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { + self.map.entry(early_lint.node_id).or_default().push(early_lint); + } + + pub fn take(&mut self, id: NodeId) -> Vec { + // FIXME(#120456) - is `swap_remove` correct? + self.map.swap_remove(&id).unwrap_or_default() + } + + pub fn buffer_lint( + &mut self, + lint: &'static Lint, + node_id: NodeId, + span: impl Into, + decorate: impl Into, + ) { + self.add_early_lint(BufferedEarlyLint { + lint_id: LintId::of(lint), + node_id, + span: Some(span.into()), + diagnostic: decorate.into(), + }); + } +} diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 183dceddd2c..43ce886975c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -138,10 +138,20 @@ where /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. #[rustc_diagnostic_item = "LintDiagnostic"] pub trait LintDiagnostic<'a, G: EmissionGuarantee> { - /// Decorate and emit a lint. + /// Decorate a lint with the information from this type. fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); } +pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> { + fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>); +} + +impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D { + fn decorate_lint_box<'b>(self: Box, diag: &'b mut Diag<'a, G>) { + self.decorate_lint(diag); + } +} + #[derive(Clone, Debug, Encodable, Decodable)] pub(crate) struct DiagLocation { file: Cow<'static, str>, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a775b70dbee..38c5716348f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -40,9 +40,10 @@ use std::{fmt, panic}; use Level::*; pub use codes::*; +pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, - FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic, + FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, @@ -80,6 +81,7 @@ use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; pub mod codes; +mod decorate_diag; mod diagnostic; mod diagnostic_impls; pub mod emitter; diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f2c15071532..8ff21509f4a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -13,13 +13,13 @@ use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; +use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation}; use rustc_hir::def::MacroKinds; use rustc_hir::{Stability, find_attr}; -use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; +use rustc_lint_defs::RegisteredTools; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::config::CollapseMacroDebuginfo; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0ca4fcc66ca..925da03f4c2 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -8,11 +8,12 @@ use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync; +use rustc_errors::LintBuffer; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; use rustc_parse::validate_attr; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; -use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; +use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index e9bd9dccdf1..0669da1a025 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; +use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan}; use rustc_feature::Features; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -23,7 +23,7 @@ use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; -use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId}; +use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId}; use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 58205087def..dff1fc43670 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -7,10 +7,11 @@ use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; use rustc_ast::{self as ast, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; use rustc_feature::Features; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; +use rustc_session::lint::LintPass; use rustc_span::{Ident, Span}; use tracing::debug; @@ -36,8 +37,11 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.opt_span_lint(lint_id.lint, span, |diag| { - diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag); + self.context.opt_span_lint(lint_id.lint, span, |diag| match diagnostic { + DecorateDiagCompat::Builtin(b) => { + diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, b, diag); + } + DecorateDiagCompat::Dynamic(d) => d.decorate_lint_box(diag), }); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index f06757b3c23..bdbac7fc4d1 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -133,10 +133,9 @@ pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder; pub use passes::{EarlyLintPass, LateLintPass}; +pub use rustc_errors::BufferedEarlyLint; pub use rustc_session::lint::Level::{self, *}; -pub use rustc_session::lint::{ - BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec, -}; +pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index d1f5cc21277..c1eb31cfa17 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,8 +3,7 @@ use std::borrow::Cow; use rustc_abi::ExternAbi; use rustc_ast::AttrId; use rustc_ast::attr::AttributeExt; -use rustc_ast::node_id::NodeId; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; @@ -818,55 +817,6 @@ pub enum BuiltinLintDiag { }, } -/// Lints that are buffered up early on in the `Session` before the -/// `LintLevels` is calculated. -#[derive(Debug)] -pub struct BufferedEarlyLint { - /// The span of code that we are linting on. - pub span: Option, - - /// The `NodeId` of the AST node that generated the lint. - pub node_id: NodeId, - - /// A lint Id that can be passed to - /// `rustc_lint::early::EarlyContextAndPass::check_id`. - pub lint_id: LintId, - - /// Customization of the `Diag<'_>` for the lint. - pub diagnostic: BuiltinLintDiag, -} - -#[derive(Default, Debug)] -pub struct LintBuffer { - pub map: FxIndexMap>, -} - -impl LintBuffer { - pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) { - self.map.entry(early_lint.node_id).or_default().push(early_lint); - } - - pub fn take(&mut self, id: NodeId) -> Vec { - // FIXME(#120456) - is `swap_remove` correct? - self.map.swap_remove(&id).unwrap_or_default() - } - - pub fn buffer_lint( - &mut self, - lint: &'static Lint, - node_id: NodeId, - span: impl Into, - diagnostic: BuiltinLintDiag, - ) { - self.add_early_lint(BufferedEarlyLint { - lint_id: LintId::of(lint), - node_id, - span: Some(span.into()), - diagnostic, - }); - } -} - pub type RegisteredTools = FxIndexSet; /// Declares a static item of type `&'static Lint`. diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 4a19cf1563c..18520089e3e 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_errors::{Applicability, Diag, EmissionGuarantee}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer}; use rustc_feature::GateIssue; use rustc_hir::attrs::{DeprecatedSince, Deprecation}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -12,7 +12,7 @@ use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stabil use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_session::Session; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; -use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; +use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint}; use rustc_session::parse::feature_err_issue; use rustc_span::{Span, Symbol, sym}; use tracing::debug; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e70c98ab704..a7298af502e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -32,7 +32,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -46,7 +46,6 @@ use rustc_macros::{ }; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; -use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, sym}; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2063c46124c..9407ce9d398 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -49,7 +49,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard}; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed}; +use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::attrs::StrippedCfgItem; @@ -72,8 +72,8 @@ use rustc_middle::ty::{ ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, }; use rustc_query_system::ich::StableHashingContext; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; -use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 62891eb4f26..852ae414157 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -99,7 +99,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { EXPLICIT_BUILTIN_CFGS_IN_FLAGS, None, ast::CRATE_NODE_ID, - BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }, + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.into(), ) }; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 426480f0dba..9048c51d5b4 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -11,8 +11,8 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination}; use rustc_errors::translation::Translator; use rustc_errors::{ - ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, - StashKey, + BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, + DiagMessage, EmissionGuarantee, MultiSpan, StashKey, }; use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_span::edition::Edition; @@ -27,7 +27,7 @@ use crate::errors::{ FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler, }; use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION; -use crate::lint::{BufferedEarlyLint, BuiltinLintDiag, Lint, LintId}; +use crate::lint::{Lint, LintId}; /// Collected spans during parsing for places where a certain feature was /// used and should be feature gated accordingly in `check_crate`. @@ -342,17 +342,17 @@ impl ParseSess { lint: &'static Lint, span: impl Into, node_id: NodeId, - diagnostic: BuiltinLintDiag, + diagnostic: impl Into, ) { - self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic) + self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic.into()) } - pub fn opt_span_buffer_lint( + pub(crate) fn opt_span_buffer_lint( &self, lint: &'static Lint, span: Option, node_id: NodeId, - diagnostic: BuiltinLintDiag, + diagnostic: DecorateDiagCompat, ) { self.buffered_lints.with_lock(|buffered_lints| { buffered_lints.push(BufferedEarlyLint { -- cgit 1.4.1-3-g733a5 From 3bf61444616fc0b9de1e09031f40be0943823fc5 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Mon, 11 Aug 2025 11:46:30 +0200 Subject: Allow errors to be emitted as fatal during attribute parsing --- compiler/rustc_attr_parsing/src/context.rs | 40 ++++++++++++++++++++-------- compiler/rustc_attr_parsing/src/interface.rs | 2 +- compiler/rustc_attr_parsing/src/parser.rs | 23 +++++++++------- compiler/rustc_errors/src/diagnostic.rs | 23 ++++++++++++++++ 4 files changed, 66 insertions(+), 22 deletions(-) (limited to 'compiler/rustc_errors/src') diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index bb701df6053..f30edbfaaa1 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,7 +5,7 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; -use rustc_errors::Diagnostic; +use rustc_errors::{Diag, Diagnostic, Level}; use rustc_feature::AttributeTemplate; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; @@ -263,11 +263,7 @@ impl Stage for Early { sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>, ) -> ErrorGuaranteed { - if self.emit_errors.should_emit() { - sess.dcx().emit_err(diag) - } else { - sess.dcx().create_err(diag).delay_as_bug() - } + self.should_emit().emit_err_or_delay(sess.dcx().create_err(diag)) } fn should_emit(&self) -> ShouldEmit { @@ -333,7 +329,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { /// 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) { - if !self.stage.should_emit().should_emit() { + if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { return; } let id = self.target_id; @@ -651,8 +647,13 @@ pub enum OmitDoc { Skip, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] pub enum ShouldEmit { + /// The operations will emit errors, and lints, and errors are fatal. + /// + /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. + /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. + EarlyFatal, /// The operation will emit errors and lints. /// This is usually what you need. ErrorsAndLints, @@ -662,10 +663,27 @@ pub enum ShouldEmit { } impl ShouldEmit { - pub fn should_emit(&self) -> bool { + pub(crate) fn emit_err_or_delay(&self, diag: Diag<'_>) -> ErrorGuaranteed { + match self { + ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(), + ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(), + ShouldEmit::ErrorsAndLints => diag.emit(), + ShouldEmit::Nothing => diag.delay_as_bug(), + } + } + + pub(crate) fn maybe_emit_err(&self, diag: Diag<'_>) { match self { - ShouldEmit::ErrorsAndLints => true, - ShouldEmit::Nothing => false, + ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => { + diag.emit(); + } + ShouldEmit::EarlyFatal => { + diag.upgrade_to_fatal().emit(); + } + ShouldEmit::ErrorsAndLints => { + diag.emit(); + } + ShouldEmit::Nothing => {} } } } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f652d39a708..b4222e41b71 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -252,7 +252,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { (accept.accept_fn)(&mut cx, args); - if self.stage.should_emit().should_emit() { + if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) { self.check_target( path.get_attribute_path(), attr.span, diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 364f8819d13..3ba188938c6 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -328,15 +328,16 @@ fn expr_to_lit( match res { Ok(lit) => { if token_lit.suffix.is_some() { - psess - .dcx() - .create_err(SuffixedLiteralInAttribute { span: lit.span }) - .emit_unless_delay(!should_emit.should_emit()); + should_emit.emit_err_or_delay( + psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); None } else { - if should_emit.should_emit() && !lit.kind.is_unsuffixed() { + if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there - psess.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span }); + should_emit.maybe_emit_err( + psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); } Some(lit) @@ -366,7 +367,7 @@ fn expr_to_lit( err.downgrade_to_delayed_bug(); } - err.emit_unless_delay(!should_emit.should_emit()); + should_emit.emit_err_or_delay(err); None } } @@ -397,9 +398,11 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { } }; - if self.should_emit.should_emit() && !lit.kind.is_unsuffixed() { + if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there - self.parser.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span }); + self.should_emit.maybe_emit_err( + self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }), + ); } Ok(lit) @@ -539,7 +542,7 @@ impl<'a> MetaItemListParser<'a> { ) { Ok(s) => Some(s), Err(e) => { - e.emit_unless_delay(!should_emit.should_emit()); + should_emit.emit_err_or_delay(e); None } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 43ce886975c..96a4ed3218f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -577,6 +577,29 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.level = Level::DelayedBug; } + /// Make emitting this diagnostic fatal + /// + /// Changes the level of this diagnostic to Fatal, and importantly also changes the emission guarantee. + /// This is sound for errors that would otherwise be printed, but now simply exit the process instead. + /// This function still gives an emission guarantee, the guarantee is now just that it exits fatally. + /// For delayed bugs this is different, since those are buffered. If we upgrade one to fatal, another + /// might now be ignored. + #[rustc_lint_diagnostics] + #[track_caller] + pub fn upgrade_to_fatal(mut self) -> Diag<'a, FatalAbort> { + assert!( + matches!(self.level, Level::Error), + "upgrade_to_fatal: cannot upgrade {:?} to Fatal: not an error", + self.level + ); + self.level = Level::Fatal; + + // Take is okay since we immediately rewrap it in another diagnostic. + // i.e. we do emit it despite defusing the original diagnostic's drop bomb. + let diag = self.diag.take(); + Diag { dcx: self.dcx, diag, _marker: PhantomData } + } + with_fn! { with_span_label, /// Appends a labeled span to the diagnostic. /// -- cgit 1.4.1-3-g733a5 From 4b35cde904c1015df42b2c63244c1db1ed51fff9 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Mon, 11 Aug 2025 11:46:30 +0200 Subject: Support lints in early attribute parsing --- Cargo.lock | 1 - compiler/rustc_attr_parsing/src/interface.rs | 32 ++++++++--- compiler/rustc_attr_parsing/src/lints.rs | 6 +-- .../rustc_attr_parsing/src/session_diagnostics.rs | 6 +-- compiler/rustc_errors/Cargo.toml | 1 - compiler/rustc_errors/src/lib.rs | 6 +-- compiler/rustc_middle/src/ty/context.rs | 2 + compiler/rustc_session/src/session.rs | 17 +++++- .../lint/unused/unused_attributes-must_use.fixed | 2 + tests/ui/lint/unused/unused_attributes-must_use.rs | 2 + .../lint/unused/unused_attributes-must_use.stderr | 63 ++++++++++++---------- 11 files changed, 93 insertions(+), 45 deletions(-) (limited to 'compiler/rustc_errors/src') 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 @@ -50,6 +50,27 @@ impl<'sess> AttributeParser<'sess, Early> { target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, + ) -> Option { + 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 { let mut parsed = Self::parse_limited_all( sess, @@ -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(lint: &AttributeLint, lint_emitter: L) { +pub fn emit_attribute_lint(lint: &AttributeLint, lint_emitter: L) { let AttributeLint { id, span, kind } = lint; match kind { @@ -51,7 +51,7 @@ pub fn emit_attribute_lint(lint: &AttributeLint, 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, - 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, } +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, + 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 -- cgit 1.4.1-3-g733a5