From f5a2bb6263ad332ceb2d9b3b82afe935259a7723 Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Mon, 2 Mar 2026 20:28:16 +0000 Subject: [PATCH 1/9] Add hygiene annotations for tokens in macro_rules! bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `-Zunpretty=expanded,hygiene` was not printing syntax context annotations for identifiers and lifetimes inside `macro_rules!` bodies. These tokens are printed via `print_tt()` → `token_to_string_ext()`, which converts tokens to strings without calling `ann_post()`. This meant that macro-generated `macro_rules!` definitions with hygienic metavar parameters (e.g. multiple `$marg` distinguished only by hygiene) were printed with no way to tell them apart. This was fixed by adding a match on `token.kind` in `print_tt()` to call `ann_post()` for `Ident`, `NtIdent`, `Lifetime`, and `NtLifetime` tokens, matching how `print_ident()` and `print_lifetime()` already handle AST-level identifiers and lifetimes. Signed-off-by: Andrew V. Teylu --- compiler/rustc_ast_pretty/src/pprust/state.rs | 17 ++++++ .../hygiene/unpretty-debug-lifetimes.stdout | 8 +-- tests/ui/hygiene/unpretty-debug-metavars.rs | 25 +++++++++ .../ui/hygiene/unpretty-debug-metavars.stdout | 52 +++++++++++++++++++ tests/ui/hygiene/unpretty-debug.stdout | 10 +++- tests/ui/proc-macro/meta-macro-hygiene.stdout | 5 +- .../nonterminal-token-hygiene.stdout | 17 ++++-- 7 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 tests/ui/hygiene/unpretty-debug-metavars.rs create mode 100644 tests/ui/hygiene/unpretty-debug-metavars.stdout diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f4168301bc5df..7d963dd2037c5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -735,6 +735,23 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere TokenTree::Token(token, spacing) => { let token_str = self.token_to_string_ext(token, convert_dollar_crate); self.word(token_str); + // Emit hygiene annotations for identity-bearing tokens, + // matching how print_ident() and print_lifetime() call ann_post(). + match token.kind { + token::Ident(name, _) => { + self.ann_post(Ident::new(name, token.span)); + } + token::NtIdent(ident, _) => { + self.ann_post(ident); + } + token::Lifetime(name, _) => { + self.ann_post(Ident::new(name, token.span)); + } + token::NtLifetime(ident, _) => { + self.ann_post(ident); + } + _ => {} + } if let token::DocComment(..) = token.kind { self.hardbreak() } diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout index 28a5c70a02d79..689453326c0b5 100644 --- a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout @@ -7,15 +7,17 @@ // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" -#![feature /* 0#0 */(decl_macro)] -#![feature /* 0#0 */(no_core)] +#![feature /* 0#0 */(decl_macro /* 0#0 */)] +#![feature /* 0#0 */(no_core /* 0#0 */)] #![no_core /* 0#0 */] macro lifetime_hygiene /* 0#0 */ { - ($f:ident<$a:lifetime>) => { fn $f<$a, 'a>() {} } + ($f /* 0#0 */:ident /* 0#0 */<$a /* 0#0 */:lifetime /* 0#0 */>) + => + { fn /* 0#0 */ $f /* 0#0 */<$a /* 0#0 */, 'a /* 0#0 */>() {} } } fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {} diff --git a/tests/ui/hygiene/unpretty-debug-metavars.rs b/tests/ui/hygiene/unpretty-debug-metavars.rs new file mode 100644 index 0000000000000..a0c47bec3ccf7 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-metavars.rs @@ -0,0 +1,25 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test: metavar parameters in macro-generated macro_rules! +// definitions should have hygiene annotations so that textually identical +// `$marg` bindings are distinguishable by their syntax contexts. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(no_core)] +#![no_core] + +macro_rules! make_macro { + (@inner $name:ident ($dol:tt) $a:ident) => { + macro_rules! $name { + ($dol $a : expr, $dol marg : expr) => {} + } + }; + ($name:ident) => { + make_macro!{@inner $name ($) marg} + }; +} + +make_macro!(add2); diff --git a/tests/ui/hygiene/unpretty-debug-metavars.stdout b/tests/ui/hygiene/unpretty-debug-metavars.stdout new file mode 100644 index 0000000000000..307c5e1b8de97 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-metavars.stdout @@ -0,0 +1,52 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test: metavar parameters in macro-generated macro_rules! +// definitions should have hygiene annotations so that textually identical +// `$marg` bindings are distinguishable by their syntax contexts. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(no_core /* 0#0 */)] +#![no_core /* 0#0 */] + +macro_rules! make_macro + /* + 0#0 + */ { + (@inner /* 0#0 */ $name /* 0#0 */:ident /* 0#0 + */($dol /* 0#0 */:tt /* 0#0 */) $a /* 0#0 */:ident /* 0#0 */) + => + { + macro_rules /* 0#0 */! $name /* 0#0 */ + { + ($dol /* 0#0 */ $a /* 0#0 */ : expr /* 0#0 */, $dol /* + 0#0 */ marg /* 0#0 */ : expr /* 0#0 */) => {} + } + }; ($name /* 0#0 */:ident /* 0#0 */) => + { + make_macro /* 0#0 + */!{@inner /* 0#0 */ $name /* 0#0 */($) marg /* 0#0 */} + }; +} +macro_rules! add2 + /* + 0#0 + */ { + ($ marg /* 0#1 */ : expr /* 0#2 */, $marg /* 0#2 */ : expr /* + 0#2 */) => {} +} + + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "make_macro") +crate0::{{expn2}}: parent: crate0::{{expn1}}, call_site_ctxt: #1, def_site_ctxt: #0, kind: Macro(Bang, "make_macro") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) +#2: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque) +*/ diff --git a/tests/ui/hygiene/unpretty-debug.stdout b/tests/ui/hygiene/unpretty-debug.stdout index f35bd7a7cb2ca..ac6051e2d542e 100644 --- a/tests/ui/hygiene/unpretty-debug.stdout +++ b/tests/ui/hygiene/unpretty-debug.stdout @@ -5,10 +5,16 @@ //@ normalize-stdout: "\d+#" -> "0#" // minimal junk -#![feature /* 0#0 */(no_core)] +#![feature /* 0#0 */(no_core /* 0#0 */)] #![no_core /* 0#0 */] -macro_rules! foo /* 0#0 */ { ($x: ident) => { y + $x } } +macro_rules! foo + /* + 0#0 + */ { + ($x /* 0#0 */: ident /* 0#0 */) => + { y /* 0#0 */ + $x /* 0#0 */ } +} fn bar /* 0#0 */() { let x /* 0#0 */ = 1; diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index b5db9922b31a8..a03e567bd48aa 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -1,7 +1,7 @@ Def site: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:26:37: 26:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:26:43: 26:44 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:26:44: 26:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:26:45: 26:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:26:50: 26:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:26:51: 26:53 (#3) }] Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }] -#![feature /* 0#0 */(prelude_import)] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] //@ aux-build:make-macro.rs //@ proc-macro: meta-macro.rs //@ edition:2018 @@ -30,7 +30,8 @@ macro_rules! produce_it */ { () => { - meta_macro::print_def_site!($crate::dummy!()); + meta_macro /* 0#0 */::print_def_site /* 0#0 + */!($crate /* 0#0 */::dummy /* 0#0 */!()); // `print_def_site!` will respan the `$crate` identifier // with `Span::def_site()`. This should cause it to resolve // relative to `meta_macro`, *not* `make_macro` (despite diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index e45abab03b4c4..61b55782e6e9d 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -20,7 +20,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/nonterminal-token-hygiene.rs:23:27: 23:32 (#4), }, ] -#![feature /* 0#0 */(prelude_import)] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] #![no_std /* 0#0 */] // Make sure that marks from declarative macros are applied to tokens in nonterminal. @@ -34,7 +34,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ //@ proc-macro: test-macros.rs //@ edition: 2015 -#![feature /* 0#0 */(decl_macro)] +#![feature /* 0#0 */(decl_macro /* 0#0 */)] #![no_std /* 0#0 */] extern crate core /* 0#2 */; #[prelude_import /* 0#1 */] @@ -49,15 +49,22 @@ macro_rules! outer /* 0#0 */ { - ($item:item) => + ($item /* 0#0 */:item /* 0#0 */) => { - macro inner() { print_bang! { $item } } inner!(); + macro /* 0#0 */ inner /* 0#0 */() + { print_bang /* 0#0 */! { $item /* 0#0 */ } } inner /* 0#0 + */!(); }; } struct S /* 0#0 */; -macro inner /* 0#3 */ { () => { print_bang! { struct S; } } } +macro inner + /* + 0#3 + */ { + () => { print_bang /* 0#3 */! { struct /* 0#0 */ S /* 0#0 */; } } +} struct S /* 0#5 */; // OK, not a duplicate definition of `S` From bf6db4f345799e1f922b3109a7278d8be11f3058 Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Tue, 3 Mar 2026 13:06:55 +0000 Subject: [PATCH 2/9] Add regression tests for token hygiene annotations in macro bodies Add `unpretty-debug-shadow` test covering macro body tokens that reference a shadowed variable, and simplify the `unpretty-debug-metavars` test macro. Signed-off-by: Andrew V. Teylu --- tests/ui/hygiene/unpretty-debug-metavars.rs | 7 +++-- .../ui/hygiene/unpretty-debug-metavars.stdout | 7 +++-- tests/ui/hygiene/unpretty-debug-shadow.rs | 20 +++++++++++++ tests/ui/hygiene/unpretty-debug-shadow.stdout | 30 +++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 tests/ui/hygiene/unpretty-debug-shadow.rs create mode 100644 tests/ui/hygiene/unpretty-debug-shadow.stdout diff --git a/tests/ui/hygiene/unpretty-debug-metavars.rs b/tests/ui/hygiene/unpretty-debug-metavars.rs index a0c47bec3ccf7..41bf75cd0d98b 100644 --- a/tests/ui/hygiene/unpretty-debug-metavars.rs +++ b/tests/ui/hygiene/unpretty-debug-metavars.rs @@ -1,9 +1,10 @@ //@ check-pass //@ compile-flags: -Zunpretty=expanded,hygiene -// Regression test: metavar parameters in macro-generated macro_rules! -// definitions should have hygiene annotations so that textually identical -// `$marg` bindings are distinguishable by their syntax contexts. +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, metavar parameters in macro-generated macro_rules! definitions +// were missing hygiene annotations, making identical `$marg` bindings +// indistinguishable. // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" diff --git a/tests/ui/hygiene/unpretty-debug-metavars.stdout b/tests/ui/hygiene/unpretty-debug-metavars.stdout index 307c5e1b8de97..89658bc909a13 100644 --- a/tests/ui/hygiene/unpretty-debug-metavars.stdout +++ b/tests/ui/hygiene/unpretty-debug-metavars.stdout @@ -1,9 +1,10 @@ //@ check-pass //@ compile-flags: -Zunpretty=expanded,hygiene -// Regression test: metavar parameters in macro-generated macro_rules! -// definitions should have hygiene annotations so that textually identical -// `$marg` bindings are distinguishable by their syntax contexts. +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, metavar parameters in macro-generated macro_rules! definitions +// were missing hygiene annotations, making identical `$marg` bindings +// indistinguishable. // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" diff --git a/tests/ui/hygiene/unpretty-debug-shadow.rs b/tests/ui/hygiene/unpretty-debug-shadow.rs new file mode 100644 index 0000000000000..2aa68c8aec110 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-shadow.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, tokens in macro_rules! bodies were missing hygiene annotations, +// making it impossible to see how a macro's reference to a shadowed variable +// is distinguished from the shadowing binding. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(no_core)] +#![no_core] + +fn f() { + let x = 0; + macro_rules! use_x { () => { x }; } + let x = 1; + use_x!(); +} diff --git a/tests/ui/hygiene/unpretty-debug-shadow.stdout b/tests/ui/hygiene/unpretty-debug-shadow.stdout new file mode 100644 index 0000000000000..36076b6a968fe --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-shadow.stdout @@ -0,0 +1,30 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, tokens in macro_rules! bodies were missing hygiene annotations, +// making it impossible to see how a macro's reference to a shadowed variable +// is distinguished from the shadowing binding. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(no_core /* 0#0 */)] +#![no_core /* 0#0 */] + +fn f /* 0#0 */() { + let x /* 0#0 */ = 0; + macro_rules! use_x /* 0#0 */ { () => { x /* 0#0 */ }; } + let x /* 0#0 */ = 1; + x /* 0#1 */; +} + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "use_x") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) +*/ From 7ec77fd4d5cb035b81a8477c50763c727ceb09c1 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Mar 2026 20:29:39 +0900 Subject: [PATCH 3/9] Fix invalid suggestion on `for-loops-over-fallibles` --- .../src/for_loops_over_fallibles.rs | 2 ++ .../macro-iterator-next.rs | 21 ++++++++++++++++ .../macro-iterator-next.stderr | 24 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs create mode 100644 tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index fe95a682c6376..24220e035b1dd 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -77,6 +77,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { }; let sub = if let Some(recv) = extract_iterator_next_call(cx, arg) + && recv.span.can_be_used_for_suggestions() + && recv.span.between(arg_span.shrink_to_hi()).can_be_used_for_suggestions() && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { ForLoopsOverFalliblesLoopSub::RemoveNext { diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs new file mode 100644 index 0000000000000..0fe88734099c7 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs @@ -0,0 +1,21 @@ +// This test ensures that the `for-loops-over-fallibles` lint doesn't suggest +// removing `next`. +// ref. + +#![forbid(for_loops_over_fallibles)] +//~^ NOTE: the lint level is defined here + +fn main() { + macro_rules! mac { + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in mac!(next [1, 2]) {} + //~^ ERROR: for loop over an `Option`. This is more readably written as an `if let` statement + //~| NOTE: in this expansion of desugaring of `for` loop + //~| NOTE: in this expansion of desugaring of `for` loop + //~| HELP: to check pattern in a loop use `while let` + //~| HELP: consider using `if let` to clear intent +} diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr new file mode 100644 index 0000000000000..36848fffa4394 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr @@ -0,0 +1,24 @@ +error: for loop over an `Option`. This is more readably written as an `if let` statement + --> $DIR/macro-iterator-next.rs:15:14 + | +LL | for _ in mac!(next [1, 2]) {} + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/macro-iterator-next.rs:5:11 + | +LL | #![forbid(for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: to check pattern in a loop use `while let` + | +LL - for _ in mac!(next [1, 2]) {} +LL + while let Some(_) = mac!(next [1, 2]) {} + | +help: consider using `if let` to clear intent + | +LL - for _ in mac!(next [1, 2]) {} +LL + if let Some(_) = mac!(next [1, 2]) {} + | + +error: aborting due to 1 previous error + From 924374c75b6ec99c5d95847f6c54554d8d590716 Mon Sep 17 00:00:00 2001 From: Redddy Date: Tue, 17 Mar 2026 06:30:04 +0000 Subject: [PATCH 4/9] mGCA: Lower const generic args to infer when needed --- compiler/rustc_ast_lowering/src/lib.rs | 10 +++-- .../const-generics/mgca/braced-const-infer.rs | 9 ++++ .../mgca/braced-const-infer.stderr | 9 ++++ .../mgca/macro-const-arg-infer.rs | 20 +++++++++ .../mgca/macro-const-arg-infer.stderr | 44 +++++++++++++++++++ 5 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 tests/ui/const-generics/mgca/braced-const-infer.rs create mode 100644 tests/ui/const-generics/mgca/braced-const-infer.stderr create mode 100644 tests/ui/const-generics/mgca/macro-const-arg-infer.rs create mode 100644 tests/ui/const-generics/mgca/macro-const-arg-infer.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e8092b540ec87..7a0537b0e7c6e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1269,9 +1269,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) } - ast::GenericArg::Const(ct) => GenericArg::Const( - self.lower_anon_const_to_const_arg_and_alloc(ct).try_as_ambig_ct().unwrap(), - ), + ast::GenericArg::Const(ct) => { + let ct = self.lower_anon_const_to_const_arg_and_alloc(ct); + match ct.try_as_ambig_ct() { + Some(ct) => GenericArg::Const(ct), + None => GenericArg::Infer(hir::InferArg { hir_id: ct.hir_id, span: ct.span }), + } + } } } diff --git a/tests/ui/const-generics/mgca/braced-const-infer.rs b/tests/ui/const-generics/mgca/braced-const-infer.rs new file mode 100644 index 0000000000000..9b007d338d1da --- /dev/null +++ b/tests/ui/const-generics/mgca/braced-const-infer.rs @@ -0,0 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/153198 +#![feature(min_generic_const_args)] +#![allow(incomplete_features, rust_2021_compatibility)] + +trait Trait {} + +impl dyn Trait<{_}> {} //~ ERROR: the placeholder `_` is not allowed within types on item signatures + +fn main() {} diff --git a/tests/ui/const-generics/mgca/braced-const-infer.stderr b/tests/ui/const-generics/mgca/braced-const-infer.stderr new file mode 100644 index 0000000000000..d8ffee4cd0e8f --- /dev/null +++ b/tests/ui/const-generics/mgca/braced-const-infer.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/braced-const-infer.rs:7:17 + | +LL | impl dyn Trait<{_}> {} + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/macro-const-arg-infer.rs b/tests/ui/const-generics/mgca/macro-const-arg-infer.rs new file mode 100644 index 0000000000000..017c63d8b0db5 --- /dev/null +++ b/tests/ui/const-generics/mgca/macro-const-arg-infer.rs @@ -0,0 +1,20 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/153198 +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +macro_rules! y { + ( $($matcher:tt)*) => { + _ //~ ERROR: the placeholder `_` is not allowed within types on item signatures + }; +} + +struct A; //~ ERROR: type parameter `T` is never used + +const y: A< + { + y! { + x + } + }, +> = 1; //~ ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr b/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr new file mode 100644 index 0000000000000..406206c59e18f --- /dev/null +++ b/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr @@ -0,0 +1,44 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/macro-const-arg-infer.rs:10:10 + | +LL | struct A; + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0308]: mismatched types + --> $DIR/macro-const-arg-infer.rs:18:5 + | +LL | const y: A< + | __________- +LL | | { +LL | | y! { +LL | | x +LL | | } +LL | | }, +LL | | > = 1; + | | - ^ expected `A<_>`, found integer + | |_| + | expected because of the type of the constant + | + = note: expected struct `A<_>` + found type `{integer}` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants + --> $DIR/macro-const-arg-infer.rs:6:9 + | +LL | _ + | ^ not allowed in type signatures +... +LL | / y! { +LL | | x +LL | | } + | |_________- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0121, E0308, E0392. +For more information about an error, try `rustc --explain E0121`. From 11810b8dd9d5ea1dad36eb5f6c77fa5d95871a81 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 8 Mar 2026 10:49:21 +0500 Subject: [PATCH 5/9] fix inference variables leaking into HIR const lowering logic --- .../src/hir_ty_lowering/mod.rs | 6 ++++++ .../mgca/infer-vars-in-const-args.rs | 16 ++++++++++++++ .../mgca/infer-vars-in-const-args.stderr | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tests/ui/const-generics/mgca/infer-vars-in-const-args.rs create mode 100644 tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index bf97bfb1ebbce..00791ed194c52 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2874,6 +2874,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> Const<'tcx> { let tcx = self.tcx(); + + if ty.has_infer() { + let e = self.dcx().span_err(span, "inference variables are not supported in constants"); + return ty::Const::new_error(tcx, e); + } + if let LitKind::Err(guar) = *kind { return ty::Const::new_error(tcx, guar); } diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs new file mode 100644 index 0000000000000..cbc59d461694f --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs @@ -0,0 +1,16 @@ +#![allow(incomplete_features)] +#![feature(adt_const_params, min_generic_const_args, generic_const_parameter_types)] + +fn main() { + foo::<_, { 2 }>(); + //~^ ERROR: inference variables are not supported in constants + let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; + //~^ ERROR: inference variables are not supported in constants +} + +struct PC { +//~^ ERROR: `T` can't be used as a const parameter type [E0741] + a: T, +} + +fn foo() {} diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr b/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr new file mode 100644 index 0000000000000..0f64582c1cef6 --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr @@ -0,0 +1,21 @@ +error[E0741]: `T` can't be used as a const parameter type + --> $DIR/infer-vars-in-const-args.rs:11:23 + | +LL | struct PC { + | ^ + +error: inference variables are not supported in constants + --> $DIR/infer-vars-in-const-args.rs:5:16 + | +LL | foo::<_, { 2 }>(); + | ^ + +error: inference variables are not supported in constants + --> $DIR/infer-vars-in-const-args.rs:7:20 + | +LL | let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; + | ^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0741`. From 5113488163ac3fdbb2adb665308b0bd3f524c44f Mon Sep 17 00:00:00 2001 From: human9000 Date: Tue, 17 Mar 2026 09:36:08 +0500 Subject: [PATCH 6/9] Improve `ty::Infer` handling during const literal lowering Co-authored-by: BoxyUwU --- .../src/hir_ty_lowering/mod.rs | 15 +++++++------ compiler/rustc_middle/src/ty/consts/lit.rs | 5 ++++- .../src/builder/expr/as_constant.rs | 5 ++++- compiler/rustc_mir_build/src/thir/constant.rs | 22 ++++++++++--------- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- compiler/rustc_ty_utils/src/consts.rs | 3 ++- ...> infer-vars-in-const-args-conflicting.rs} | 9 ++++++-- ...fer-vars-in-const-args-conflicting.stderr} | 10 ++++----- .../mgca/infer-vars-in-const-args-correct.rs | 22 +++++++++++++++++++ 9 files changed, 65 insertions(+), 28 deletions(-) rename tests/ui/const-generics/mgca/{infer-vars-in-const-args.rs => infer-vars-in-const-args-conflicting.rs} (52%) rename tests/ui/const-generics/mgca/{infer-vars-in-const-args.stderr => infer-vars-in-const-args-conflicting.stderr} (58%) create mode 100644 tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 00791ed194c52..9396bf6352c59 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2875,10 +2875,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Const<'tcx> { let tcx = self.tcx(); - if ty.has_infer() { - let e = self.dcx().span_err(span, "inference variables are not supported in constants"); - return ty::Const::new_error(tcx, e); - } + let ty = if !ty.has_infer() { Some(ty) } else { None }; if let LitKind::Err(guar) = *kind { return ty::Const::new_error(tcx, guar); @@ -2911,16 +2908,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: false }), + hir::ExprKind::Lit(lit) => { + Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: false }) + } hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: true }), + hir::ExprKind::Lit(lit) => { + Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: true }) + } _ => None, }, _ => None, }; lit_input.and_then(|l| { - if const_lit_matches_ty(tcx, &l.lit, l.ty, l.neg) { + if const_lit_matches_ty(tcx, &l.lit, ty, l.neg) { tcx.at(expr.span) .lit_to_const(l) .map(|value| ty::Const::new_value(tcx, value.valtree, value.ty)) diff --git a/compiler/rustc_middle/src/ty/consts/lit.rs b/compiler/rustc_middle/src/ty/consts/lit.rs index be6dfb20e0433..7be225a9e9215 100644 --- a/compiler/rustc_middle/src/ty/consts/lit.rs +++ b/compiler/rustc_middle/src/ty/consts/lit.rs @@ -10,7 +10,10 @@ pub struct LitToConstInput<'tcx> { /// The absolute value of the resultant constant. pub lit: LitKind, /// The type of the constant. - pub ty: Ty<'tcx>, + /// + /// `None` is used by const generics when the type of the constant is unknown, e.g. + /// if there are inference variables + pub ty: Option>, /// If the constant is negative. pub neg: bool, } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index ad6c1f7dce5b8..c67d99a8eb7de 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -50,7 +50,8 @@ pub(crate) fn as_constant_inner<'tcx>( match *kind { ExprKind::Literal { lit, neg } => { - let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg }); + let const_ = + lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty: Some(ty), neg }); ConstOperand { span, user_ty: None, const_ } } @@ -109,6 +110,8 @@ pub(crate) fn as_constant_inner<'tcx>( fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>) -> Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; + let ty = ty.expect("type of literal must be known at this point"); + if let Err(guar) = ty.error_reported() { return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index b4eedb15033c8..019af24613541 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -31,25 +31,27 @@ pub(crate) fn lit_to_const<'tcx>( .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)) }; - let (valtree, valtree_ty) = match (lit, expected_ty.kind()) { + let (valtree, valtree_ty) = match (lit, expected_ty.map(|ty| ty.kind())) { (ast::LitKind::Str(s, _), _) => { let str_bytes = s.as_str().as_bytes(); let valtree_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); (ty::ValTree::from_raw_bytes(tcx, str_bytes), valtree_ty) } - (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(byte_sym, _), Some(ty::Ref(_, inner_ty, _))) if let ty::Slice(ty) | ty::Array(ty, _) = inner_ty.kind() && let ty::Uint(UintTy::U8) = ty.kind() => { - (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty) + (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) } - (ast::LitKind::ByteStr(byte_sym, _), ty::Slice(inner_ty) | ty::Array(inner_ty, _)) - if tcx.features().deref_patterns() - && let ty::Uint(UintTy::U8) = inner_ty.kind() => + ( + ast::LitKind::ByteStr(byte_sym, _), + Some(ty::Slice(inner_ty) | ty::Array(inner_ty, _)), + ) if tcx.features().deref_patterns() + && let ty::Uint(UintTy::U8) = inner_ty.kind() => { // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is // enabled, in order to allow, e.g., `deref!(b"..."): Vec`. - (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty) + (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) } (ast::LitKind::ByteStr(byte_sym, _), _) => { let valtree = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()); @@ -79,11 +81,11 @@ pub(crate) fn lit_to_const<'tcx>( trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }, i.to_unsigned()); (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_int(tcx, i)) } - (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Uint(ui)) if !neg => { + (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Uint(ui))) if !neg => { let scalar_int = trunc(n.get(), *ui); (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, *ui)) } - (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Int(i)) => { + (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Int(i))) => { // Unsigned "negation" has the same bitwise effect as signed negation, // which gets the result we want without additional casts. let scalar_int = @@ -101,7 +103,7 @@ pub(crate) fn lit_to_const<'tcx>( let bits = parse_float_into_scalar(n, fty, neg)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, fty)) } - (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), ty::Float(fty)) => { + (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), Some(ty::Float(fty))) => { let bits = parse_float_into_scalar(n, *fty, neg)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, *fty)) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 67cde0e2c8866..fec2fb9ca5719 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -696,7 +696,7 @@ impl<'tcx> PatCtxt<'tcx> { // patterns to `str`, and byte-string literal patterns to `[u8; N]` or `[u8]`. let pat_ty = self.typeck_results.node_type(pat.hir_id); - let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated }; + let lit_input = LitToConstInput { lit: lit.node, ty: Some(pat_ty), neg: *negated }; let constant = const_lit_matches_ty(self.tcx, &lit.node, pat_ty, *negated) .then(|| self.tcx.at(expr.span).lit_to_const(lit_input)) .flatten() diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 1d444079f8968..49e0bdde37870 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -58,7 +58,8 @@ fn recurse_build<'tcx>( } &ExprKind::Literal { lit, neg } => { let sp = node.span; - match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: node.ty, neg }) { + match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: Some(node.ty), neg }) + { Some(value) => ty::Const::new_value(tcx, value.valtree, value.ty), None => ty::Const::new_misc_error(tcx), } diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs similarity index 52% rename from tests/ui/const-generics/mgca/infer-vars-in-const-args.rs rename to tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs index cbc59d461694f..eeb76683eedf1 100644 --- a/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs @@ -1,11 +1,16 @@ +//! This test ensures compilation failure when trying to pass literals +//! without explicitly stated type as inference variables in generic arguments. +//! +//! See https://github.com/rust-lang/rust/pull/153557 + #![allow(incomplete_features)] #![feature(adt_const_params, min_generic_const_args, generic_const_parameter_types)] fn main() { foo::<_, { 2 }>(); - //~^ ERROR: inference variables are not supported in constants + //~^ ERROR: type annotations needed for the literal let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; - //~^ ERROR: inference variables are not supported in constants + //~^ ERROR: type annotations needed for the literal } struct PC { diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr similarity index 58% rename from tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr rename to tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr index 0f64582c1cef6..a4b13f41aef07 100644 --- a/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr @@ -1,17 +1,17 @@ error[E0741]: `T` can't be used as a const parameter type - --> $DIR/infer-vars-in-const-args.rs:11:23 + --> $DIR/infer-vars-in-const-args-conflicting.rs:16:23 | LL | struct PC { | ^ -error: inference variables are not supported in constants - --> $DIR/infer-vars-in-const-args.rs:5:16 +error: type annotations needed for the literal + --> $DIR/infer-vars-in-const-args-conflicting.rs:10:16 | LL | foo::<_, { 2 }>(); | ^ -error: inference variables are not supported in constants - --> $DIR/infer-vars-in-const-args.rs:7:20 +error: type annotations needed for the literal + --> $DIR/infer-vars-in-const-args-conflicting.rs:12:20 | LL | let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; | ^^ diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs new file mode 100644 index 0000000000000..f1c0e0b2b4c30 --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs @@ -0,0 +1,22 @@ +//! This test ensures no errors are emitted when lowering literals with +//! explicitly stated types and inference variables in the type of the const +//! generic parameter. +//! +//! See https://github.com/rust-lang/rust/pull/153557 + +//@check-pass + +#![allow(incomplete_features)] +#![feature(adt_const_params, + min_generic_const_args, + generic_const_parameter_types, + unsized_const_params +)] + +use std::marker::ConstParamTy_; + +fn main() { + foo::<_, 2_i32>(); +} + +fn foo() {} From b4c1677fff43d9ea04002de85aef77915092be7d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Mar 2026 20:34:25 +0900 Subject: [PATCH 7/9] Do not lint `for-loops-over-fallibles` on external macros --- .../rustc_lint/src/for_loops_over_fallibles.rs | 5 +++++ tests/crashes/147973.rs | 14 -------------- .../auxiliary/external-macro-issue-148114.rs | 13 +++++++++++++ .../external-macro-issue-148114.rs | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 14 deletions(-) delete mode 100644 tests/crashes/147973.rs create mode 100644 tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs create mode 100644 tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 24220e035b1dd..a7c5943c250b8 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -49,6 +49,11 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some((pat, arg)) = extract_for_loop(expr) else { return }; + // Do not put suggestions for external macros. + if pat.span.from_expansion() { + return; + } + let arg_span = arg.span.source_callsite(); let ty = cx.typeck_results().expr_ty(arg); diff --git a/tests/crashes/147973.rs b/tests/crashes/147973.rs deleted file mode 100644 index 7271c54846f15..0000000000000 --- a/tests/crashes/147973.rs +++ /dev/null @@ -1,14 +0,0 @@ -// This is part of series of regression tests for some diagnostics ICEs encountered in the wild with -// suggestions having overlapping parts under https://github.com/rust-lang/rust/pull/146121. -// This is one MCVE from the beta crater run regressions from issue 147973. - -//@ needs-rustc-debug-assertions -//@ known-bug: #147973 - -//@ aux-build: overlapping_spans_helper.rs -extern crate overlapping_spans_helper; - -fn main() { - let _name = Some(1); - overlapping_spans_helper::do_loop!(_name); -} diff --git a/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs new file mode 100644 index 0000000000000..68eff33cb3f10 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs @@ -0,0 +1,13 @@ +#[macro_export] +macro_rules! identity { + ($x:ident) => { + $x + }; +} + +#[macro_export] +macro_rules! do_loop { + ($x:ident) => { + for $crate::identity!($x) in $x {} + }; +} diff --git a/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs new file mode 100644 index 0000000000000..ca3ac3b9a8647 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ aux-build:external-macro-issue-148114.rs + +// This test ensures we do not trigger the lint on external macros +// ref. + +#![deny(for_loops_over_fallibles)] + +extern crate external_macro_issue_148114 as dep; + +fn main() { + let _name = Some(1); + dep::do_loop!(_name); +} From cfe18cfd37e3aefe0a9a653d3faac825c3a7868a Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Mar 2026 20:35:16 +0900 Subject: [PATCH 8/9] Correct dir name --- .../auxiliary/external-macro-issue-148114.rs | 0 .../external-macro-issue-148114.rs | 0 .../macro-issue-140747.rs | 0 .../macro-issue-140747.stderr | 0 .../macro-iterator-next.rs | 0 .../macro-iterator-next.stderr | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/auxiliary/external-macro-issue-148114.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/external-macro-issue-148114.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-issue-140747.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-issue-140747.stderr (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-iterator-next.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-iterator-next.stderr (100%) diff --git a/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs rename to tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs diff --git a/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs rename to tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs b/tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs rename to tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr b/tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.stderr similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr rename to tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.stderr diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs rename to tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr rename to tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr From c8a2c46eda3d8a8cc0dbe92002543ab5867586a3 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 16 Mar 2026 14:33:39 +0100 Subject: [PATCH 9/9] bootstrap: Optionally print a backtrace if a command fails --- src/bootstrap/src/utils/exec.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index b264c961b6593..f9b0d2dbb2095 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -7,6 +7,7 @@ //! relevant to command execution in the bootstrap process. This includes settings such as //! dry-run mode, verbosity level, and failure behavior. +use std::backtrace::{Backtrace, BacktraceStatus}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Formatter}; @@ -930,6 +931,16 @@ Executed at: {executed_at}"#, if stderr.captures() { writeln!(error_message, "\n--- STDERR vvv\n{}", output.stderr().trim()).unwrap(); } + let backtrace = if exec_ctx.verbosity > 1 { + Backtrace::force_capture() + } else if matches!(command.failure_behavior, BehaviorOnFailure::Ignore) { + Backtrace::disabled() + } else { + Backtrace::capture() + }; + if matches!(backtrace.status(), BacktraceStatus::Captured) { + writeln!(error_message, "\n--- BACKTRACE vvv\n{backtrace}").unwrap(); + } match command.failure_behavior { BehaviorOnFailure::DelayFail => {