From 9fb9225cf498b6f215502df4bf0023809c2f3056 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Sun, 15 Feb 2026 14:41:46 +0300 Subject: [PATCH 1/6] Disable debug_assert_not_in_new_nodes for multiple threads --- compiler/rustc_middle/src/dep_graph/graph.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index 3ef0511795b90..1c79de7600990 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::QueryInvocationId; use rustc_data_structures::sharded::{self, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU64, Lock}; +use rustc_data_structures::sync::{AtomicU64, Lock, is_dyn_thread_safe}; use rustc_data_structures::unord::UnordMap; use rustc_data_structures::{assert_matches, outline}; use rustc_errors::DiagInner; @@ -1311,7 +1311,9 @@ impl CurrentDepGraph { prev_graph: &SerializedDepGraph, prev_index: SerializedDepNodeIndex, ) { - if let Some(ref nodes_in_current_session) = self.nodes_in_current_session { + if !is_dyn_thread_safe() + && let Some(ref nodes_in_current_session) = self.nodes_in_current_session + { debug_assert!( !nodes_in_current_session .lock() From 448097dd2386957a36880efbb78cb83021774540 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 13:38:34 +0000 Subject: [PATCH 2/6] Suggest async block instead of async closure when possible --- .../src/error_reporting/traits/suggestions.rs | 28 +++++++++++ .../suggest-async-block-issue-140265.rs | 20 ++++++++ .../suggest-async-block-issue-140265.stderr | 50 +++++++++++++++++++ ...arg-where-it-should-have-been-called.fixed | 14 ++++++ ...as-arg-where-it-should-have-been-called.rs | 2 + ...rg-where-it-should-have-been-called.stderr | 14 +++--- 6 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs create mode 100644 tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr create mode 100644 tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.fixed diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 176cc8c4c2cab..0e460763d9dcb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -798,6 +798,34 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return false; } + // If this is a zero-argument async closure directly passed as an argument + // and the expected type is `Future`, suggest using `async {}` block instead + // of `async || {}`. + if let ty::CoroutineClosure(def_id, args) = *self_ty.kind() + && let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder() + && let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind() + && inputs.is_empty() + && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future) + && let Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async), + fn_arg_span: Some(arg_span), + .. + }), + .. + })) = self.tcx.hir_get_if_local(def_id) + && obligation.cause.span.contains(*arg_span) + { + err.span_suggestion_verbose( + arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)), + "use `async {}` instead of `async || {}` to introduce an async block", + "", + Applicability::MachineApplicable, + ); + return true; + } + // Get the name of the callable and the arguments to be used in the suggestion. let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { diff --git a/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs new file mode 100644 index 0000000000000..9dc6153be2578 --- /dev/null +++ b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs @@ -0,0 +1,20 @@ +//@ edition:2024 +// Test that we suggest using `async {}` block instead of `async || {}` closure if possible + +use std::future::Future; + +fn takes_future(_fut: impl Future) {} + +fn main() { + // Basic case: suggest using async block + takes_future(async || { + //~^ ERROR is not a future + println!("hi!"); + }); + + // With arguments: should suggest calling the closure, not using async block + takes_future(async |x: i32| { + //~^ ERROR is not a future + println!("{x}"); + }); +} diff --git a/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr new file mode 100644 index 0000000000000..a742a358c0a3f --- /dev/null +++ b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr @@ -0,0 +1,50 @@ +error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:10:18: 10:26}` is not a future + --> $DIR/suggest-async-block-issue-140265.rs:10:18 + | +LL | takes_future(async || { + | _____------------_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | println!("hi!"); +LL | | }); + | |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:10:18: 10:26}` is not a future + | + = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:10:18: 10:26}` +note: required by a bound in `takes_future` + --> $DIR/suggest-async-block-issue-140265.rs:6:28 + | +LL | fn takes_future(_fut: impl Future) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future` +help: use `async {}` instead of `async || {}` to introduce an async block + | +LL - takes_future(async || { +LL + takes_future(async { + | + +error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:32}` is not a future + --> $DIR/suggest-async-block-issue-140265.rs:16:18 + | +LL | takes_future(async |x: i32| { + | _____------------_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | println!("{x}"); +LL | | }); + | |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:32}` is not a future + | + = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:32}` +note: required by a bound in `takes_future` + --> $DIR/suggest-async-block-issue-140265.rs:6:28 + | +LL | fn takes_future(_fut: impl Future) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future` +help: use parentheses to call this closure + | +LL | }(/* i32 */)); + | +++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.fixed b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.fixed new file mode 100644 index 0000000000000..88a94058bd54a --- /dev/null +++ b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.fixed @@ -0,0 +1,14 @@ +//@ edition:2018 +//@ run-rustfix +#![allow(unused_variables)] +use std::future::Future; + +async fn foo() {} + +fn bar(f: impl Future) {} + +fn main() { + bar(foo()); //~ERROR E0277 + let async_closure = async || (); + bar(async_closure()); //~ERROR E0277 +} diff --git a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs index 8e67f4e7398cd..a05773497707c 100644 --- a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs +++ b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs @@ -1,4 +1,6 @@ //@ edition:2018 +//@ run-rustfix +#![allow(unused_variables)] use std::future::Future; async fn foo() {} diff --git a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index 696b156d5a5f0..761a8d529f509 100644 --- a/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/tests/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -1,5 +1,5 @@ error[E0277]: `fn() -> impl Future {foo}` is not a future - --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:9:9 + --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:9 | LL | bar(foo); | --- ^^^ `fn() -> impl Future {foo}` is not a future @@ -8,7 +8,7 @@ LL | bar(foo); | = help: the trait `Future` is not implemented for fn item `fn() -> impl Future {foo}` note: required by a bound in `bar` - --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:6:16 + --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:8:16 | LL | fn bar(f: impl Future) {} | ^^^^^^^^^^^^^^^^^ required by this bound in `bar` @@ -17,17 +17,17 @@ help: use parentheses to call this function LL | bar(foo()); | ++ -error[E0277]: `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:25: 10:33}` is not a future - --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:9 +error[E0277]: `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:25: 12:33}` is not a future + --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:13:9 | LL | bar(async_closure); - | --- ^^^^^^^^^^^^^ `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:25: 10:33}` is not a future + | --- ^^^^^^^^^^^^^ `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:25: 12:33}` is not a future | | | required by a bound introduced by this call | - = help: the trait `Future` is not implemented for `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:25: 10:33}` + = help: the trait `Future` is not implemented for `{async closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:25: 12:33}` note: required by a bound in `bar` - --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:6:16 + --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:8:16 | LL | fn bar(f: impl Future) {} | ^^^^^^^^^^^^^^^^^ required by this bound in `bar` From 37364585a10e368918e4a79466be24354ff5cf87 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 5 Feb 2026 10:09:46 +0000 Subject: [PATCH 3/6] Fix async closure suggestion when no space between || and { --- .../src/error_reporting/traits/suggestions.rs | 14 ++++++-- .../suggest-async-block-issue-140265.rs | 6 ++++ .../suggest-async-block-issue-140265.stderr | 32 ++++++++++++++++--- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 0e460763d9dcb..386c75dad9dd3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -800,7 +800,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If this is a zero-argument async closure directly passed as an argument // and the expected type is `Future`, suggest using `async {}` block instead - // of `async || {}`. + // of `async || {}` if let ty::CoroutineClosure(def_id, args) = *self_ty.kind() && let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder() && let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind() @@ -817,8 +817,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { })) = self.tcx.hir_get_if_local(def_id) && obligation.cause.span.contains(*arg_span) { + let sm = self.tcx.sess.source_map(); + let removal_span = if let Ok(snippet) = + sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))) + && snippet.ends_with(' ') + { + // There's a space after `||`, include it in the removal + arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)) + } else { + *arg_span + }; err.span_suggestion_verbose( - arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)), + removal_span, "use `async {}` instead of `async || {}` to introduce an async block", "", Applicability::MachineApplicable, diff --git a/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs index 9dc6153be2578..b5120d196f4ee 100644 --- a/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs +++ b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.rs @@ -12,6 +12,12 @@ fn main() { println!("hi!"); }); + // Without space between `||` and `{`: should also suggest using async block + takes_future(async||{ + //~^ ERROR is not a future + println!("no space!"); + }); + // With arguments: should suggest calling the closure, not using async block takes_future(async |x: i32| { //~^ ERROR is not a future diff --git a/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr index a742a358c0a3f..d81cfaac7f7d1 100644 --- a/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr +++ b/tests/ui/async-await/async-closures/suggest-async-block-issue-140265.stderr @@ -22,9 +22,33 @@ LL - takes_future(async || { LL + takes_future(async { | -error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:32}` is not a future +error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:25}` is not a future --> $DIR/suggest-async-block-issue-140265.rs:16:18 | +LL | takes_future(async||{ + | _____------------_^ + | | | + | | required by a bound introduced by this call +LL | | +LL | | println!("no space!"); +LL | | }); + | |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:25}` is not a future + | + = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:25}` +note: required by a bound in `takes_future` + --> $DIR/suggest-async-block-issue-140265.rs:6:28 + | +LL | fn takes_future(_fut: impl Future) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `takes_future` +help: use `async {}` instead of `async || {}` to introduce an async block + | +LL - takes_future(async||{ +LL + takes_future(async{ + | + +error[E0277]: `{async closure@$DIR/suggest-async-block-issue-140265.rs:22:18: 22:32}` is not a future + --> $DIR/suggest-async-block-issue-140265.rs:22:18 + | LL | takes_future(async |x: i32| { | _____------------_^ | | | @@ -32,9 +56,9 @@ LL | takes_future(async |x: i32| { LL | | LL | | println!("{x}"); LL | | }); - | |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:32}` is not a future + | |_____^ `{async closure@$DIR/suggest-async-block-issue-140265.rs:22:18: 22:32}` is not a future | - = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:16:18: 16:32}` + = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-async-block-issue-140265.rs:22:18: 22:32}` note: required by a bound in `takes_future` --> $DIR/suggest-async-block-issue-140265.rs:6:28 | @@ -45,6 +69,6 @@ help: use parentheses to call this closure LL | }(/* i32 */)); | +++++++++++ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. From d41e16bceaad145c82fcd0b38b4a316ca7a29984 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 28 Feb 2026 18:38:30 +0100 Subject: [PATCH 4/6] rustfmt: add test for field representing type builtin syntax --- src/tools/rustfmt/tests/target/field-representing-types.rs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/tools/rustfmt/tests/target/field-representing-types.rs diff --git a/src/tools/rustfmt/tests/target/field-representing-types.rs b/src/tools/rustfmt/tests/target/field-representing-types.rs new file mode 100644 index 0000000000000..a31ccbfb461f2 --- /dev/null +++ b/src/tools/rustfmt/tests/target/field-representing-types.rs @@ -0,0 +1,2 @@ +type FRT = builtin # field_of(Type, ident); +type FRT2 = builtin # field_of(Type, ident); From af299893fdbf2bef1716705f7c458ea61eb4ddf0 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 28 Feb 2026 21:37:32 +0000 Subject: [PATCH 5/6] Introduce --ci flag in tidy * add --ci flag in tidy This commit introduces --ci flag in tidy because currently bootstrap can't pass its ci env information to tidy. It also modifies how CiInfo initialize its ci_env variable. tidy codes which uses CiEnv::is_ci for checking ci are now using ci_env in CiInfo. * address review - Fix comment - Use Option for ci flag in order to have true/false explicitly or unspecified (implicit false) * integrate CiInfo into TidyCtx * remove CiInfo * CiEnv::current() should be called when ci flag is not added * extract base_commit() to a separate function * use &TidyCtx instead of clone --- src/bootstrap/src/core/build_steps/test.rs | 3 + src/tools/tidy/src/alphabetical/tests.rs | 4 +- src/tools/tidy/src/arg_parser.rs | 3 + src/tools/tidy/src/arg_parser/tests.rs | 46 +++++++++++++ src/tools/tidy/src/deps.rs | 7 +- src/tools/tidy/src/diagnostics.rs | 73 +++++++++++++++++++-- src/tools/tidy/src/error_codes.rs | 8 +-- src/tools/tidy/src/extdeps.rs | 2 +- src/tools/tidy/src/extra_checks/mod.rs | 31 +++++---- src/tools/tidy/src/lib.rs | 75 +++------------------- src/tools/tidy/src/main.rs | 9 ++- src/tools/tidy/src/rustdoc_json.rs | 8 ++- 12 files changed, 168 insertions(+), 101 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index f0fe1c03e7e14..f223ecf49507c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1320,6 +1320,9 @@ impl Step for Tidy { if builder.config.cmd.bless() { cmd.arg("--bless"); } + if builder.config.is_running_on_ci() { + cmd.arg("--ci=true"); + } if let Some(s) = builder.config.cmd.extra_checks().or(builder.config.tidy_extra_checks.as_deref()) { diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs index 5fa0dd751b647..c1192986e63f2 100644 --- a/src/tools/tidy/src/alphabetical/tests.rs +++ b/src/tools/tidy/src/alphabetical/tests.rs @@ -5,7 +5,7 @@ use crate::diagnostics::{TidyCtx, TidyFlags}; #[track_caller] fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) { - let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::default()); + let tidy_ctx = TidyCtx::new(Path::new("/"), false, None, TidyFlags::default()); let mut check = tidy_ctx.start_check("alphabetical-test"); check_lines(Path::new(name), lines, &tidy_ctx, &mut check); @@ -37,7 +37,7 @@ fn bless_test(before: &str, after: &str) { let temp_path = tempfile::Builder::new().tempfile().unwrap().into_temp_path(); std::fs::write(&temp_path, before).unwrap(); - let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::new(true)); + let tidy_ctx = TidyCtx::new(Path::new("/"), false, None, TidyFlags::new(true)); let mut check = tidy_ctx.start_check("alphabetical-test"); check_lines(&temp_path, before, &tidy_ctx, &mut check); diff --git a/src/tools/tidy/src/arg_parser.rs b/src/tools/tidy/src/arg_parser.rs index 8041f739308d4..04502ac6266b6 100644 --- a/src/tools/tidy/src/arg_parser.rs +++ b/src/tools/tidy/src/arg_parser.rs @@ -15,6 +15,7 @@ pub struct TidyArgParser { pub npm: PathBuf, pub verbose: bool, pub bless: bool, + pub ci: Option, pub extra_checks: Option>, pub pos_args: Vec, } @@ -59,6 +60,7 @@ impl TidyArgParser { ) .arg(Arg::new("verbose").help("verbose").long("verbose").action(ArgAction::SetTrue)) .arg(Arg::new("bless").help("target files are modified").long("bless").action(ArgAction::SetTrue)) + .arg(Arg::new("ci").help("ci flag").long("ci").default_missing_value("true").num_args(0..=1).value_parser(value_parser!(bool))) .arg( Arg::new("extra_checks") .help("extra checks") @@ -78,6 +80,7 @@ impl TidyArgParser { npm: matches.get_one::("npm").unwrap().clone(), verbose: *matches.get_one::("verbose").unwrap(), bless: *matches.get_one::("bless").unwrap(), + ci: matches.get_one::("ci").cloned(), extra_checks: None, pos_args: vec![], }; diff --git a/src/tools/tidy/src/arg_parser/tests.rs b/src/tools/tidy/src/arg_parser/tests.rs index c5e7aed21c1a0..3aa6162f5c7d6 100644 --- a/src/tools/tidy/src/arg_parser/tests.rs +++ b/src/tools/tidy/src/arg_parser/tests.rs @@ -19,6 +19,7 @@ fn test_tidy_parser_full() { "yarn", "--verbose", "--bless", + "--ci", "--extra-checks", "if-installed:auto:js,auto:if-installed:py,if-installed:auto:cpp,if-installed:auto:spellcheck", "--", // pos_args @@ -38,6 +39,8 @@ fn test_tidy_parser_full() { assert_eq!(parsed_args.npm, PathBuf::from("yarn")); assert!(parsed_args.verbose); assert!(parsed_args.bless); + assert!(parsed_args.ci.is_some()); + assert!(parsed_args.ci.unwrap()); assert_eq!( parsed_args.extra_checks, Some(vec![ @@ -166,3 +169,46 @@ fn test_tidy_parser_missing_npm_path() { let cmd = TidyArgParser::command(); assert!(cmd.try_get_matches_from(args).is_err()); } + +// --ci has some variations +#[test] +fn test_tidy_parse_ci_flag() { + // They are requried + let base_args = vec![ + "rust-tidy", + "--root-path", + "/home/user/rust", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + "--output-dir", + "/home/user/rust/build", + "--concurrency", + "16", + "--npm-path", + "yarn", + ]; + + // No --ci + let parsed_args = TidyArgParser::build(TidyArgParser::command().get_matches_from(&base_args)); + assert!(parsed_args.ci.is_none()); + + // --ci + let mut args1 = base_args.clone(); + args1.push("--ci"); + let parsed_args = TidyArgParser::build(TidyArgParser::command().get_matches_from(args1)); + assert!(parsed_args.ci.is_some()); + + // --ci=true + let mut args2 = base_args.clone(); + args2.extend_from_slice(&["--ci", "true"]); + let parsed_args = TidyArgParser::build(TidyArgParser::command().get_matches_from(args2)); + assert!(parsed_args.ci.is_some()); + assert!(parsed_args.ci.unwrap()); + + // --ci=false + let mut args2 = base_args.clone(); + args2.extend_from_slice(&["--ci", "false"]); + let parsed_args = TidyArgParser::build(TidyArgParser::command().get_matches_from(args2)); + assert!(parsed_args.ci.is_some()); + assert!(!parsed_args.ci.unwrap()); +} diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 24c610b41f3a4..2879df6670e81 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -6,7 +6,6 @@ use std::fs::{File, read_dir}; use std::io::Write; use std::path::Path; -use build_helper::ci::CiEnv; use cargo_metadata::semver::Version; use cargo_metadata::{Metadata, Package, PackageId}; @@ -637,7 +636,7 @@ pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) { check_proc_macro_dep_list(root, cargo, bless, &mut check); for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES { - if has_missing_submodule(root, submodules) { + if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) { continue; } @@ -757,8 +756,8 @@ pub static CRATES: &[&str] = &[ /// Used to skip a check if a submodule is not checked out, and not in a CI environment. /// /// This helps prevent enforcing developers to fetch submodules for tidy. -pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool { - !CiEnv::is_ci() +pub fn has_missing_submodule(root: &Path, submodules: &[&str], is_ci: bool) -> bool { + !is_ci && submodules.iter().any(|submodule| { let path = root.join(submodule); !path.exists() diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index 4e6c316f5e18e..3f93316f63606 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -4,6 +4,9 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use build_helper::ci::CiEnv; +use build_helper::git::{GitConfig, get_closest_upstream_commit}; +use build_helper::stage0_parser::{Stage0Config, parse_stage0_file}; use termcolor::Color; /// CLI flags used by tidy. @@ -28,11 +31,24 @@ impl TidyFlags { pub struct TidyCtx { tidy_flags: TidyFlags, diag_ctx: Arc>, + ci_env: CiEnv, + pub base_commit: Option, } impl TidyCtx { - pub fn new(root_path: &Path, verbose: bool, tidy_flags: TidyFlags) -> Self { - Self { + pub fn new( + root_path: &Path, + verbose: bool, + ci_flag: Option, + tidy_flags: TidyFlags, + ) -> Self { + let ci_env = match ci_flag { + Some(true) => CiEnv::GitHubActions, + Some(false) => CiEnv::None, + None => CiEnv::current(), + }; + + let mut tidy_ctx = Self { diag_ctx: Arc::new(Mutex::new(DiagCtxInner { running_checks: Default::default(), finished_checks: Default::default(), @@ -40,13 +56,22 @@ impl TidyCtx { verbose, })), tidy_flags, - } + ci_env, + base_commit: None, + }; + tidy_ctx.base_commit = find_base_commit(&tidy_ctx); + + tidy_ctx } pub fn is_bless_enabled(&self) -> bool { self.tidy_flags.bless } + pub fn is_running_on_ci(&self) -> bool { + self.ci_env.is_running_in_ci() + } + pub fn start_check>(&self, id: Id) -> RunningCheck { let mut id = id.into(); @@ -75,6 +100,46 @@ impl TidyCtx { } } +fn find_base_commit(tidy_ctx: &TidyCtx) -> Option { + let mut check = tidy_ctx.start_check("CI history"); + + let stage0 = parse_stage0_file(); + let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config; + + let base_commit = match get_closest_upstream_commit( + None, + &GitConfig { + nightly_branch: &nightly_branch, + git_merge_commit_email: &git_merge_commit_email, + }, + tidy_ctx.ci_env, + ) { + Ok(Some(commit)) => Some(commit), + Ok(None) => { + error_if_in_ci("no base commit found", tidy_ctx.is_running_on_ci(), &mut check); + None + } + Err(error) => { + error_if_in_ci( + &format!("failed to retrieve base commit: {error}"), + tidy_ctx.is_running_on_ci(), + &mut check, + ); + None + } + }; + + base_commit +} + +fn error_if_in_ci(msg: &str, is_ci: bool, check: &mut RunningCheck) { + if is_ci { + check.error(msg); + } else { + check.warning(format!("{msg}. Some checks will be skipped.")); + } +} + struct DiagCtxInner { running_checks: HashSet, finished_checks: HashSet, @@ -175,7 +240,7 @@ impl RunningCheck { /// Useful if you want to run some functions from tidy without configuring /// diagnostics. pub fn new_noop() -> Self { - let ctx = TidyCtx::new(Path::new(""), false, TidyFlags::default()); + let ctx = TidyCtx::new(Path::new(""), false, None, TidyFlags::default()); ctx.start_check("noop") } diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 185f3187a15c8..76fbf79951a9c 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -36,11 +36,11 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E07 const IGNORE_UI_TEST_CHECK: &[&str] = &["E0461", "E0465", "E0514", "E0554", "E0640", "E0717", "E0729"]; -pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, tidy_ctx: TidyCtx) { +pub fn check(root_path: &Path, search_paths: &[&Path], tidy_ctx: TidyCtx) { let mut check = tidy_ctx.start_check("error_codes"); // Check that no error code explanation was removed. - check_removed_error_code_explanation(ci_info, &mut check); + check_removed_error_code_explanation(&tidy_ctx.base_commit, &mut check); // Stage 1: create list let error_codes = extract_error_codes(root_path, &mut check); @@ -57,8 +57,8 @@ pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, check_error_codes_used(search_paths, &error_codes, &mut check, &no_longer_emitted); } -fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, check: &mut RunningCheck) { - let Some(base_commit) = &ci_info.base_commit else { +fn check_removed_error_code_explanation(base_commit: &Option, check: &mut RunningCheck) { + let Some(base_commit) = base_commit else { check.verbose_msg("Skipping error code explanation removal check"); return; }; diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 19c773d12f7fa..7999386a3c298 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -19,7 +19,7 @@ pub fn check(root: &Path, tidy_ctx: TidyCtx) { let mut check = tidy_ctx.start_check("extdeps"); for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { - if crate::deps::has_missing_submodule(root, submodules) { + if crate::deps::has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) { continue; } diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 28e78b396d557..124de884637ea 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -23,9 +23,6 @@ use std::process::Command; use std::str::FromStr; use std::{env, fmt, fs, io}; -use build_helper::ci::CiEnv; - -use crate::CiInfo; use crate::diagnostics::TidyCtx; mod rustdoc_js; @@ -53,7 +50,6 @@ const SPELLCHECK_VER: &str = "1.38.1"; pub fn check( root_path: &Path, outdir: &Path, - ci_info: &CiInfo, librustdoc_path: &Path, tools_path: &Path, npm: &Path, @@ -67,7 +63,6 @@ pub fn check( if let Err(e) = check_impl( root_path, outdir, - ci_info, librustdoc_path, tools_path, npm, @@ -83,7 +78,6 @@ pub fn check( fn check_impl( root_path: &Path, outdir: &Path, - ci_info: &CiInfo, librustdoc_path: &Path, tools_path: &Path, npm: &Path, @@ -121,9 +115,12 @@ fn check_impl( }; lint_args.retain(|ck| ck.is_non_if_installed_or_matches(root_path, outdir)); if lint_args.iter().any(|ck| ck.auto) { - crate::files_modified_batch_filter(ci_info, &mut lint_args, |ck, path| { - ck.is_non_auto_or_matches(path) - }); + crate::files_modified_batch_filter( + &tidy_ctx.base_commit, + tidy_ctx.is_running_on_ci(), + &mut lint_args, + |ck, path| ck.is_non_auto_or_matches(path), + ); } macro_rules! extra_check { @@ -321,7 +318,7 @@ fn check_impl( } else { eprintln!("spellchecking files"); } - let res = spellcheck_runner(root_path, &outdir, &cargo, &args); + let res = spellcheck_runner(root_path, &outdir, &cargo, &args, tidy_ctx.is_running_on_ci()); if res.is_err() { rerun_with_bless("spellcheck", "fix typos"); } @@ -629,9 +626,16 @@ fn spellcheck_runner( outdir: &Path, cargo: &Path, args: &[&str], + is_ci: bool, ) -> Result<(), Error> { - let bin_path = - ensure_version_or_cargo_install(outdir, cargo, "typos-cli", "typos", SPELLCHECK_VER)?; + let bin_path = ensure_version_or_cargo_install( + outdir, + cargo, + "typos-cli", + "typos", + SPELLCHECK_VER, + is_ci, + )?; match Command::new(bin_path).current_dir(src_root).args(args).status() { Ok(status) => { if status.success() { @@ -713,6 +717,7 @@ fn ensure_version_or_cargo_install( pkg_name: &str, bin_name: &str, version: &str, + is_ci: bool, ) -> Result { if let Ok(bin_path) = ensure_version(build_dir, bin_name, version) { return Ok(bin_path); @@ -746,7 +751,7 @@ fn ensure_version_or_cargo_install( // On CI, we set opt-level flag for quicker installation. // Since lower opt-level decreases the tool's performance, // we don't set this option on local. - if CiEnv::is_ci() { + if is_ci { cmd.env("RUSTFLAGS", "-Copt-level=0"); } diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index ed41130f5d299..2cb8df782d28e 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -6,12 +6,6 @@ use std::ffi::OsStr; use std::process::Command; -use build_helper::ci::CiEnv; -use build_helper::git::{GitConfig, get_closest_upstream_commit}; -use build_helper::stage0_parser::{Stage0Config, parse_stage0_file}; - -use crate::diagnostics::{RunningCheck, TidyCtx}; - macro_rules! static_regex { ($re:literal) => {{ static RE: ::std::sync::LazyLock<::regex::Regex> = @@ -42,60 +36,6 @@ macro_rules! t { }; } -pub struct CiInfo { - pub git_merge_commit_email: String, - pub nightly_branch: String, - pub base_commit: Option, - pub ci_env: CiEnv, -} - -impl CiInfo { - pub fn new(tidy_ctx: TidyCtx) -> Self { - let mut check = tidy_ctx.start_check("CI history"); - - let stage0 = parse_stage0_file(); - let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config; - - let mut info = Self { - nightly_branch, - git_merge_commit_email, - ci_env: CiEnv::current(), - base_commit: None, - }; - let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) { - Ok(Some(commit)) => Some(commit), - Ok(None) => { - info.error_if_in_ci("no base commit found", &mut check); - None - } - Err(error) => { - info.error_if_in_ci( - &format!("failed to retrieve base commit: {error}"), - &mut check, - ); - None - } - }; - info.base_commit = base_commit; - info - } - - pub fn git_config(&self) -> GitConfig<'_> { - GitConfig { - nightly_branch: &self.nightly_branch, - git_merge_commit_email: &self.git_merge_commit_email, - } - } - - pub fn error_if_in_ci(&self, msg: &str, check: &mut RunningCheck) { - if self.ci_env.is_running_in_ci() { - check.error(msg); - } else { - check.warning(format!("{msg}. Some checks will be skipped.")); - } - } -} - pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option { let output = Command::new("git").arg("diff").arg(base_commit).arg(extra_arg).output().ok()?; Some(String::from_utf8_lossy(&output.stdout).into()) @@ -107,15 +47,16 @@ pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option( - ci_info: &CiInfo, + base_commit: &Option, + is_ci: bool, items: &mut Vec, pred: impl Fn(&T, &str) -> bool, ) { - if CiEnv::is_ci() { + if is_ci { // assume everything is modified on CI because we really don't want false positives there. return; } - let Some(base_commit) = &ci_info.base_commit else { + let Some(base_commit) = base_commit else { eprintln!("No base commit, assuming all files are modified"); return; }; @@ -150,9 +91,13 @@ pub fn files_modified_batch_filter( } /// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files. -pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool { +pub fn files_modified( + base_commit: &Option, + is_ci: bool, + pred: impl Fn(&str) -> bool, +) -> bool { let mut v = vec![()]; - files_modified_batch_filter(ci_info, &mut v, |_, p| pred(p)); + files_modified_batch_filter(base_commit, is_ci, &mut v, |_, p| pred(p)); !v.is_empty() } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 09c08e1baf503..0e9885138d1e1 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -40,11 +40,11 @@ fn main() { let verbose = parsed_args.verbose; let bless = parsed_args.bless; + let ci = parsed_args.ci; let extra_checks = parsed_args.extra_checks; let pos_args = parsed_args.pos_args; - let tidy_ctx = TidyCtx::new(&root_path, verbose, TidyFlags::new(bless)); - let ci_info = CiInfo::new(tidy_ctx.clone()); + let tidy_ctx = TidyCtx::new(&root_path, verbose, ci, TidyFlags::new(bless)); let drain_handles = |handles: &mut VecDeque>| { // poll all threads for completion before awaiting the oldest one @@ -101,12 +101,12 @@ fn main() { check!(rustdoc_gui_tests, &tests_path); check!(rustdoc_css_themes, &librustdoc_path); check!(rustdoc_templates, &librustdoc_path); - check!(rustdoc_json, &src_path, &ci_info); + check!(rustdoc_json, &src_path); check!(known_bug, &crashes_path); check!(unknown_revision, &tests_path); // Checks that only make sense for the compiler. - check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info); + check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path]); check!(target_policy, &root_path); check!(gcc_submodule, &root_path, &compiler_path); @@ -154,7 +154,6 @@ fn main() { extra_checks, &root_path, &output_directory, - &ci_info, &librustdoc_path, &tools_path, &npm, diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs index b8fb04f2d4e1f..ef7dabc8021ba 100644 --- a/src/tools/tidy/src/rustdoc_json.rs +++ b/src/tools/tidy/src/rustdoc_json.rs @@ -8,16 +8,18 @@ use crate::diagnostics::{CheckId, TidyCtx}; const RUSTDOC_JSON_TYPES: &str = "src/rustdoc-json-types"; -pub fn check(src_path: &Path, ci_info: &crate::CiInfo, tidy_ctx: TidyCtx) { +pub fn check(src_path: &Path, tidy_ctx: TidyCtx) { let mut check = tidy_ctx.start_check(CheckId::new("rustdoc_json").path(src_path)); - let Some(base_commit) = &ci_info.base_commit else { + let Some(base_commit) = &tidy_ctx.base_commit else { check.verbose_msg("No base commit, skipping rustdoc_json check"); return; }; // First we check that `src/rustdoc-json-types` was modified. - if !crate::files_modified(ci_info, |p| p.starts_with(RUSTDOC_JSON_TYPES)) { + if !crate::files_modified(&tidy_ctx.base_commit, tidy_ctx.is_running_on_ci(), |p| { + p.starts_with(RUSTDOC_JSON_TYPES) + }) { // `rustdoc-json-types` was not modified so nothing more to check here. return; } From 2c057bb903a371769f3f68360ee1edbdcfca9c96 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 28 Feb 2026 17:21:56 +1100 Subject: [PATCH 6/6] Clean up `QueryVTable::hash_result` into `hash_value_fn` This commit: - Renames the query vtable field `hash_result` to `hash_value_fn` - Removes the unhelpful `HashResult` type alias, which was hiding an important layer of `Option` - Replaces the cryptic `hash_result!` helper macro with a more straightforward `if_no_hash!` helper, in line with other modifier-checking macros - Renames a few identifiers to refer to a query's return value as `value` --- compiler/rustc_middle/src/query/inner.rs | 6 ++-- compiler/rustc_middle/src/query/plumbing.rs | 9 ++++-- compiler/rustc_query_impl/src/execution.rs | 32 ++++++++++----------- compiler/rustc_query_impl/src/plumbing.rs | 31 +++++++++++--------- 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index bd4d5bcab4a33..aa98a0267dfc6 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -116,9 +116,9 @@ pub(crate) fn query_feed<'tcx, C>( // The query already has a cached value for this key. // That's OK if both values are the same, i.e. they have the same hash, // so now we check their hashes. - if let Some(hasher_fn) = query_vtable.hash_result { + if let Some(hash_value_fn) = query_vtable.hash_value_fn { let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| { - (hasher_fn(hcx, &old), hasher_fn(hcx, &value)) + (hash_value_fn(hcx, &old), hash_value_fn(hcx, &value)) }); if old_hash != value_hash { // We have an inconsistency. This can happen if one of the two @@ -151,7 +151,7 @@ pub(crate) fn query_feed<'tcx, C>( dep_node, tcx, &value, - query_vtable.hash_result, + query_vtable.hash_value_fn, query_vtable.format_value, ); query_vtable.cache.complete(key, value, dep_node_index); diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 55d760c55cd2f..072e29aaa9063 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -74,8 +74,6 @@ pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( pub type IsLoadableFromDiskFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; -pub type HashResult = Option, &V) -> Fingerprint>; - #[derive(Clone, Debug)] pub struct CycleError { /// The query and related span that uses the cycle. @@ -146,7 +144,12 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub try_load_from_disk_fn: Option>, pub is_loadable_from_disk_fn: Option>, - pub hash_result: HashResult, + + /// Function pointer that hashes this query's result values. + /// + /// For `no_hash` queries, this function pointer is None. + pub hash_value_fn: Option, &C::Value) -> Fingerprint>, + pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, pub format_value: fn(&C::Value) -> String, diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 22155e961426f..5b83751ae05f9 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -354,7 +354,7 @@ fn execute_job<'tcx, C: QueryCache, const INCR: bool>( debug_assert_eq!(tcx.dep_graph.is_fully_enabled(), INCR); // Delegate to another function to actually execute the query job. - let (result, dep_node_index) = if INCR { + let (value, dep_node_index) = if INCR { execute_job_incr(query, tcx, key, dep_node, id) } else { execute_job_non_incr(query, tcx, key, id) @@ -366,18 +366,18 @@ fn execute_job<'tcx, C: QueryCache, const INCR: bool>( // This can't happen, as query feeding adds the very dependencies to the fed query // as its feeding query had. So if the fed query is red, so is its feeder, which will // get evaluated first, and re-feed the query. - if let Some((cached_result, _)) = cache.lookup(&key) { - let Some(hasher) = query.hash_result else { + if let Some((cached_value, _)) = cache.lookup(&key) { + let Some(hash_value_fn) = query.hash_value_fn else { panic!( "no_hash fed query later has its value computed.\n\ Remove `no_hash` modifier to allow recomputation.\n\ The already cached value: {}", - (query.format_value)(&cached_result) + (query.format_value)(&cached_value) ); }; let (old_hash, new_hash) = tcx.with_stable_hashing_context(|mut hcx| { - (hasher(&mut hcx, &cached_result), hasher(&mut hcx, &result)) + (hash_value_fn(&mut hcx, &cached_value), hash_value_fn(&mut hcx, &value)) }); let formatter = query.format_value; if old_hash != new_hash { @@ -389,17 +389,17 @@ fn execute_job<'tcx, C: QueryCache, const INCR: bool>( computed={:#?}\nfed={:#?}", query.dep_kind, key, - formatter(&result), - formatter(&cached_result), + formatter(&value), + formatter(&cached_value), ); } } } // Tell the guard to perform completion bookkeeping, and also to not poison the query. - job_guard.complete(cache, result, dep_node_index); + job_guard.complete(cache, value, dep_node_index); - (result, Some(dep_node_index)) + (value, Some(dep_node_index)) } // Fast path for when incr. comp. is off. @@ -420,7 +420,7 @@ fn execute_job_non_incr<'tcx, C: QueryCache>( let prof_timer = tcx.prof.query_provider(); // Call the query provider. - let result = + let value = start_query(tcx, job_id, query.depth_limit, || (query.invoke_provider_fn)(tcx, key)); let dep_node_index = tcx.dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -428,14 +428,14 @@ fn execute_job_non_incr<'tcx, C: QueryCache>( // Similarly, fingerprint the result to assert that // it doesn't have anything not considered hashable. if cfg!(debug_assertions) - && let Some(hash_result) = query.hash_result + && let Some(hash_value_fn) = query.hash_value_fn { tcx.with_stable_hashing_context(|mut hcx| { - hash_result(&mut hcx, &result); + hash_value_fn(&mut hcx, &value); }); } - (result, dep_node_index) + (value, dep_node_index) } #[inline(always)] @@ -491,7 +491,7 @@ fn execute_job_incr<'tcx, C: QueryCache>( tcx, (query, key), |tcx, (query, key)| (query.invoke_provider_fn)(tcx, key), - query.hash_result, + query.hash_value_fn, ) }); @@ -542,7 +542,7 @@ fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( dep_graph_data, &value, prev_index, - query.hash_result, + query.hash_value_fn, query.format_value, ); } @@ -589,7 +589,7 @@ fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( dep_graph_data, &value, prev_index, - query.hash_result, + query.hash_value_fn, query.format_value, ); diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 8920f8dba38d1..272185845423e 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -210,19 +210,13 @@ macro_rules! is_feedable { }; } -macro_rules! hash_result { - ([][$V:ty]) => {{ - Some(|hcx, result| { - let result = rustc_middle::query::erase::restore_val::<$V>(*result); - rustc_middle::dep_graph::hash_result(hcx, &result) - }) - }}; - ([(no_hash) $($rest:tt)*][$V:ty]) => {{ - None - }}; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - hash_result!([$($modifiers)*][$($args)*]) - }; +/// Expands to `$yes` if the `no_hash` modifier is present, or `$no` otherwise. +macro_rules! if_no_hash { + ([] $yes:tt $no:tt) => { $no }; + ([(no_hash) $($modifiers:tt)*] $yes:tt $no:tt) => { $yes }; + ([$other:tt $($modifiers:tt)*] $yes:tt $no:tt) => { + if_no_hash!([$($modifiers)*] $yes $no) + } } macro_rules! call_provider { @@ -606,7 +600,16 @@ macro_rules! define_queries { let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); erase::erase_val(result) }, - hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), + hash_value_fn: if_no_hash!( + [$($modifiers)*] + None + { + Some(|hcx, erased_value: &erase::Erased>| { + let value = erase::restore_val(*erased_value); + rustc_middle::dep_graph::hash_result(hcx, &value) + }) + } + ), format_value: |value| format!("{:?}", erase::restore_val::>(*value)), description_fn: $crate::queries::_description_fns::$name, execute_query_fn: if incremental {