From 52abb8f9f0009ac951cee312fb97592bd18ab258 Mon Sep 17 00:00:00 2001 From: Asuna Date: Fri, 27 Sep 2024 02:58:14 +0800 Subject: [PATCH 001/342] Add new lint `unneeded_struct_pattern` --- CHANGELOG.md | 1 + .../src/arbitrary_source_item_ordering.rs | 4 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/unneeded_struct_pattern.rs | 76 +++++++ tests/ui/auxiliary/non-exhaustive-enum.rs | 21 ++ tests/ui/unneeded_struct_pattern.fixed | 177 +++++++++++++++ tests/ui/unneeded_struct_pattern.rs | 177 +++++++++++++++ tests/ui/unneeded_struct_pattern.stderr | 203 ++++++++++++++++++ 9 files changed, 660 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/unneeded_struct_pattern.rs create mode 100644 tests/ui/unneeded_struct_pattern.fixed create mode 100644 tests/ui/unneeded_struct_pattern.rs create mode 100644 tests/ui/unneeded_struct_pattern.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 801c96cc97ef..e8b75088c9ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6133,6 +6133,7 @@ Released 2018-09-13 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern +[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 8719f61a8903..26cc01545cab 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -445,8 +445,8 @@ fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssoc #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. use SourceItemOrderingTraitAssocItemKind::*; match value { - AssocItemKind::Const { .. } => Const, - AssocItemKind::Type { .. } => Type, + AssocItemKind::Const => Const, + AssocItemKind::Type => Type, AssocItemKind::Fn { .. } => Fn, } } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 16146a4a7a46..66e42f6f0627 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -753,6 +753,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, + crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO, crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, crate::unused_async::UNUSED_ASYNC_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 017e6e2a1423..6b4d8a4d3b27 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -374,6 +374,7 @@ mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_struct_initialization; mod unnecessary_wraps; +mod unneeded_struct_pattern; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; @@ -967,5 +968,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unneeded_struct_pattern.rs b/clippy_lints/src/unneeded_struct_pattern.rs new file mode 100644 index 000000000000..40ba70d451db --- /dev/null +++ b/clippy_lints/src/unneeded_struct_pattern.rs @@ -0,0 +1,76 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for struct patterns that match against unit variant. + /// + /// ### Why is this bad? + /// Struct pattern `{ }` or `{ .. }` is not needed for unit variant. + /// + /// ### Example + /// ```no_run + /// match Some(42) { + /// Some(v) => v, + /// None { .. } => 0, + /// }; + /// // Or + /// match Some(42) { + /// Some(v) => v, + /// None { } => 0, + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// match Some(42) { + /// Some(v) => v, + /// None => 0, + /// }; + /// ``` + #[clippy::version = "1.83.0"] + pub UNNEEDED_STRUCT_PATTERN, + style, + "using struct pattern to match against unit variant" +} + +declare_lint_pass!(UnneededStructPattern => [UNNEEDED_STRUCT_PATTERN]); + +impl LateLintPass<'_> for UnneededStructPattern { + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + if !pat.span.from_expansion() + && let PatKind::Struct(path, [], _) = &pat.kind + && let QPath::Resolved(_, path) = path + && let Res::Def(DefKind::Variant, did) = path.res + { + let enum_did = cx.tcx.parent(did); + let variant = cx.tcx.adt_def(enum_did).variant_with_id(did); + + let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty(); + let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive(); + if !has_only_fields_brackets || non_exhaustive_activated { + return; + } + + if is_from_proc_macro(cx, *path) { + return; + } + + if let Some(brackets_span) = pat.span.trim_start(path.span) { + span_lint_and_sugg( + cx, + UNNEEDED_STRUCT_PATTERN, + brackets_span, + "struct pattern is not needed for a unit variant", + "remove the struct pattern", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/tests/ui/auxiliary/non-exhaustive-enum.rs b/tests/ui/auxiliary/non-exhaustive-enum.rs index 420232f9f8d8..e3205193ccea 100644 --- a/tests/ui/auxiliary/non-exhaustive-enum.rs +++ b/tests/ui/auxiliary/non-exhaustive-enum.rs @@ -6,3 +6,24 @@ pub enum ErrorKind { #[doc(hidden)] Uncategorized, } + +#[non_exhaustive] +pub enum ExtNonExhaustiveEnum { + Unit, + Tuple(i32), + Struct { field: i32 }, +} + +pub enum ExtNonExhaustiveVariant { + ExhaustiveUnit, + #[non_exhaustive] + Unit, + #[non_exhaustive] + Tuple(i32), + #[non_exhaustive] + StructNoField {}, + #[non_exhaustive] + Struct { + field: i32, + }, +} diff --git a/tests/ui/unneeded_struct_pattern.fixed b/tests/ui/unneeded_struct_pattern.fixed new file mode 100644 index 000000000000..5bd269896a6b --- /dev/null +++ b/tests/ui/unneeded_struct_pattern.fixed @@ -0,0 +1,177 @@ +//@aux-build:non-exhaustive-enum.rs +#![allow( + clippy::manual_unwrap_or_default, + clippy::manual_unwrap_or, + clippy::redundant_pattern_matching +)] +#![warn(clippy::unneeded_struct_pattern)] + +extern crate non_exhaustive_enum; +use non_exhaustive_enum::*; + +fn noop() {} + +fn main() { + match Some(114514) { + Some(v) => v, + None => 0, + }; + + match Some(1919810) { + Some(v) => v, + None => 0, + }; + + match Some(123456) { + Some(v) => v, + None => 0, + }; + + match Some(Some(123456)) { + Some(Some(v)) => v, + Some(None) => 0, + None => 0, + }; + + if let None = Some(0) {} + if let None = Some(0) {} + if let Some(None) = Some(Some(0)) {} + let None = Some(0) else { panic!() }; + let None = Some(0) else { panic!() }; + let Some(None) = Some(Some(0)) else { panic!() }; + + enum Custom { + HasFields { + field: i32, + }, + HasBracketsNoFields {}, + NoBrackets, + #[non_exhaustive] + NoBracketsNonExhaustive, + Init, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields {} => 0, + Custom::NoBrackets => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields { .. } => 0, + Custom::NoBrackets => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets if true => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + if let Custom::HasFields { field: value } = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields {} = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields { .. } = Custom::Init { + noop(); + } + if let Custom::NoBrackets = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets | Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + + let Custom::HasFields { field: value } = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields {} = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields { .. } = Custom::Init else { + panic!() + }; + let Custom::NoBrackets = Custom::Init else { panic!() }; //~ ERROR: struct pattern is not needed for a unit variant + + let Custom::NoBrackets = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + + enum Refutable { + Variant, + } + + fn pat_in_fn_param_1(Refutable::Variant: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + fn pat_in_fn_param_2(Refutable::Variant: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + + for Refutable::Variant in [] {} //~ ERROR: struct pattern is not needed for a unit variant + for Refutable::Variant in [] {} //~ ERROR: struct pattern is not needed for a unit variant +} + +fn external_crate() { + use ExtNonExhaustiveVariant::*; + + match ExhaustiveUnit { + // Expected + ExhaustiveUnit => 0, + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + ExhaustiveUnit => 0, + // vvvvv Non-exhaustive variants, should all be ignored + Unit { .. } => 0, + Tuple { 0: field, .. } => field, + StructNoField { .. } => 0, + Struct { field, .. } => field, + _ => 0, + }; +} diff --git a/tests/ui/unneeded_struct_pattern.rs b/tests/ui/unneeded_struct_pattern.rs new file mode 100644 index 000000000000..c7658617ad37 --- /dev/null +++ b/tests/ui/unneeded_struct_pattern.rs @@ -0,0 +1,177 @@ +//@aux-build:non-exhaustive-enum.rs +#![allow( + clippy::manual_unwrap_or_default, + clippy::manual_unwrap_or, + clippy::redundant_pattern_matching +)] +#![warn(clippy::unneeded_struct_pattern)] + +extern crate non_exhaustive_enum; +use non_exhaustive_enum::*; + +fn noop() {} + +fn main() { + match Some(114514) { + Some(v) => v, + None {} => 0, + }; + + match Some(1919810) { + Some(v) => v, + None { .. } => 0, + }; + + match Some(123456) { + Some(v) => v, + None => 0, + }; + + match Some(Some(123456)) { + Some(Some(v)) => v, + Some(None {}) => 0, + None {} => 0, + }; + + if let None {} = Some(0) {} + if let None { .. } = Some(0) {} + if let Some(None {}) = Some(Some(0)) {} + let None {} = Some(0) else { panic!() }; + let None { .. } = Some(0) else { panic!() }; + let Some(None {}) = Some(Some(0)) else { panic!() }; + + enum Custom { + HasFields { + field: i32, + }, + HasBracketsNoFields {}, + NoBrackets, + #[non_exhaustive] + NoBracketsNonExhaustive, + Init, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields {} => 0, + Custom::NoBrackets {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields { .. } => 0, + Custom::NoBrackets { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets {} if true => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + if let Custom::HasFields { field: value } = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields {} = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields { .. } = Custom::Init { + noop(); + } + if let Custom::NoBrackets {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets { .. } = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive { .. } = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + + let Custom::HasFields { field: value } = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields {} = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields { .. } = Custom::Init else { + panic!() + }; + let Custom::NoBrackets {} = Custom::Init else { panic!() }; //~ ERROR: struct pattern is not needed for a unit variant + + let Custom::NoBrackets { .. } = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive {} = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive { .. } = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + + enum Refutable { + Variant, + } + + fn pat_in_fn_param_1(Refutable::Variant {}: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + fn pat_in_fn_param_2(Refutable::Variant { .. }: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + + for Refutable::Variant {} in [] {} //~ ERROR: struct pattern is not needed for a unit variant + for Refutable::Variant { .. } in [] {} //~ ERROR: struct pattern is not needed for a unit variant +} + +fn external_crate() { + use ExtNonExhaustiveVariant::*; + + match ExhaustiveUnit { + // Expected + ExhaustiveUnit => 0, + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + ExhaustiveUnit => 0, + // vvvvv Non-exhaustive variants, should all be ignored + Unit { .. } => 0, + Tuple { 0: field, .. } => field, + StructNoField { .. } => 0, + Struct { field, .. } => field, + _ => 0, + }; +} diff --git a/tests/ui/unneeded_struct_pattern.stderr b/tests/ui/unneeded_struct_pattern.stderr new file mode 100644 index 000000000000..3a7f59583802 --- /dev/null +++ b/tests/ui/unneeded_struct_pattern.stderr @@ -0,0 +1,203 @@ +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:17:13 + | +LL | None {} => 0, + | ^^^ help: remove the struct pattern + | + = note: `-D clippy::unneeded-struct-pattern` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unneeded_struct_pattern)]` + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:22:13 + | +LL | None { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:32:18 + | +LL | Some(None {}) => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:33:13 + | +LL | None {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:36:16 + | +LL | if let None {} = Some(0) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:37:16 + | +LL | if let None { .. } = Some(0) {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:38:21 + | +LL | if let Some(None {}) = Some(Some(0)) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:39:13 + | +LL | let None {} = Some(0) else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:40:13 + | +LL | let None { .. } = Some(0) else { panic!() }; + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:41:18 + | +LL | let Some(None {}) = Some(Some(0)) else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:57:27 + | +LL | Custom::NoBrackets {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:58:40 + | +LL | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:65:27 + | +LL | Custom::NoBrackets { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:66:40 + | +LL | Custom::NoBracketsNonExhaustive { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:71:27 + | +LL | Custom::NoBrackets {} if true => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:76:27 + | +LL | Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:76:64 + | +LL | Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:89:30 + | +LL | if let Custom::NoBrackets {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:93:30 + | +LL | if let Custom::NoBrackets { .. } = Custom::Init { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:97:30 + | +LL | if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:97:67 + | +LL | if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:101:43 + | +LL | if let Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:105:43 + | +LL | if let Custom::NoBracketsNonExhaustive { .. } = Custom::Init { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:121:27 + | +LL | let Custom::NoBrackets {} = Custom::Init else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:123:27 + | +LL | let Custom::NoBrackets { .. } = Custom::Init else { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:127:40 + | +LL | let Custom::NoBracketsNonExhaustive {} = Custom::Init else { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:131:40 + | +LL | let Custom::NoBracketsNonExhaustive { .. } = Custom::Init else { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:140:44 + | +LL | fn pat_in_fn_param_1(Refutable::Variant {}: Refutable) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:141:44 + | +LL | fn pat_in_fn_param_2(Refutable::Variant { .. }: Refutable) {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:143:27 + | +LL | for Refutable::Variant {} in [] {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:144:27 + | +LL | for Refutable::Variant { .. } in [] {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:158:23 + | +LL | ExhaustiveUnit { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:164:23 + | +LL | ExhaustiveUnit {} => 0, + | ^^^ help: remove the struct pattern + +error: aborting due to 33 previous errors + From 08d8c4a67fd9a9eea1bad23da92bb614bc6f9966 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Thu, 19 Dec 2024 21:56:49 +0900 Subject: [PATCH 002/342] don't suggest to use cloned for Cow in unnecessary_to_owned --- clippy_lints/src/methods/unnecessary_to_owned.rs | 3 +++ tests/ui/unnecessary_to_owned.fixed | 6 ++++++ tests/ui/unnecessary_to_owned.rs | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index d19064fd57e3..12b9d4836e8e 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -217,10 +217,13 @@ fn check_into_iter_call_arg( && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. + && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index fdcac8fb08dc..027dac419375 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -585,3 +585,9 @@ fn borrow_checks() { HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); } + +fn issue13624() -> impl IntoIterator { + let cow: Cow<'_, Vec> = Cow::Owned(vec![String::from("foo")]); + + cow.into_owned().into_iter() +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 10a9727a9a79..b89f3d552f84 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -585,3 +585,9 @@ fn borrow_checks() { HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); } + +fn issue13624() -> impl IntoIterator { + let cow: Cow<'_, Vec> = Cow::Owned(vec![String::from("foo")]); + + cow.into_owned().into_iter() +} From 7d0518c380e9dfed78855aad68d19e2e530bdcc0 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 27 Dec 2024 15:02:34 +0100 Subject: [PATCH 003/342] Implement `int_from_ascii` (#134821) Provides unstable `T::from_ascii()` and `T::from_ascii_radix()` for integer types `T`, as drafted in tracking issue #134821. To deduplicate documentation without additional macros, implementations of `isize` and `usize` no longer delegate to equivalent integer types. After #132870 they are inlined anyway. --- library/core/src/num/mod.rs | 220 +++++++++++-------- tests/ui/consts/const-eval/parse_ints.stderr | 12 +- 2 files changed, 139 insertions(+), 93 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 357a85ae843c..25c92b2d8d30 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1453,20 +1453,6 @@ pub enum FpCategory { Normal, } -macro_rules! from_str_radix_int_impl { - ($($t:ty)*) => {$( - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseIntError; - #[inline] - fn from_str(src: &str) -> Result { - <$t>::from_str_radix(src, 10) - } - } - )*} -} -from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } - /// Determines if a string of text of that length of that radix could be guaranteed to be /// stored in the given type T. /// Note that if the radix is known to the compiler, it is just the check of digits.len that @@ -1482,18 +1468,58 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -const fn from_str_radix_panic(radix: u32) -> ! { +const fn from_ascii_radix_panic(radix: u32) -> ! { const_panic!( - "from_str_radix_int: must lie in the range `[2, 36]`", - "from_str_radix_int: must lie in the range `[2, 36]` - found {radix}", + "from_ascii_radix: radix must lie in the range `[2, 36]`", + "from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}", radix: u32 = radix, ) } -macro_rules! from_str_radix { +macro_rules! from_str_int_impl { ($signedness:ident $($int_ty:ty)+) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $int_ty { + type Err = ParseIntError; + + /// Parses an integer from a string slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// use std::str::FromStr; + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # use std::str::FromStr; + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")] + /// ``` + #[inline] + fn from_str(src: &str) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_str_radix(src, 10) + } + } + impl $int_ty { - /// Converts a string slice in a given base to an integer. + /// Parses an integer from a string slice with digits in a given base. /// /// The string is expected to be an optional #[doc = sign_dependent_expr!{ @@ -1506,7 +1532,7 @@ macro_rules! from_str_radix { } }] /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) /// also represent an error. /// /// Digits are a subset of these characters, depending on `radix`: @@ -1532,11 +1558,92 @@ macro_rules! from_str_radix { #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] #[inline] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src.as_bytes(), radix) + } + + /// Parses an integer from an ASCII-byte slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src, 10) + } + + /// Parses an integer from an ASCII-byte slice with digits in a given base. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// Digits are a subset of these characters, depending on `radix`: + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; if 2 > radix || radix > 36 { - from_str_radix_panic(radix); + from_ascii_radix_panic(radix); } if src.is_empty() { @@ -1546,12 +1653,6 @@ macro_rules! from_str_radix { #[allow(unused_comparisons)] let is_signed_ty = 0 > <$int_ty>::MIN; - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - let (is_positive, mut digits) = match src { [b'+' | b'-'] => { return Err(PIE { kind: InvalidDigit }); @@ -1627,67 +1728,8 @@ macro_rules! from_str_radix { Ok(result) } } - )+} + )*} } -from_str_radix! { unsigned u8 u16 u32 u64 u128 } -from_str_radix! { signed i8 i16 i32 i64 i128 } - -// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two -// identical functions. -macro_rules! from_str_radix_size_impl { - ($($signedness:ident $t:ident $size:ty),*) => {$( - impl $size { - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional - #[doc = sign_dependent_expr!{ - $signedness ? - if signed { - " `+` or `-` " - } - if unsigned { - " `+` " - } - }] - /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) - /// also represent an error. - /// - /// Digits are a subset of these characters, depending on `radix`: - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// ``` - #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - /// Trailing space returns error: - /// ``` - #[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] - #[inline] - pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { - match <$t>::from_str_radix(src, radix) { - Ok(x) => Ok(x as $size), - Err(e) => Err(e), - } - } - })*} -} - -#[cfg(target_pointer_width = "16")] -from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize } -#[cfg(target_pointer_width = "32")] -from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize } -#[cfg(target_pointer_width = "64")] -from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize } +from_str_int_impl! { signed isize i8 i16 i32 i64 i128 } +from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 } diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr index ec9249ece8e3..189d3c3958bb 100644 --- a/tests/ui/consts/const-eval/parse_ints.stderr +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -1,8 +1,10 @@ error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/num/mod.rs:LL:COL | - = note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL + = note: the evaluated program panicked at 'from_ascii_radix: radix must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL | +note: inside `core::num::::from_ascii_radix` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_LOW` @@ -10,13 +12,15 @@ note: inside `_TOO_LOW` | LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/num/mod.rs:LL:COL | - = note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL + = note: the evaluated program panicked at 'from_ascii_radix: radix must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL | +note: inside `core::num::::from_ascii_radix` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_HIGH` @@ -24,7 +28,7 @@ note: inside `_TOO_HIGH` | LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors From 8275f42350fc394f7a35227dd19d52432f804abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Tue, 31 Dec 2024 13:30:46 +0100 Subject: [PATCH 004/342] add more test coverage for #11230 Closes #11230 changelog: nonew. --- tests/ui/crashes/ice-11230.fixed | 16 ++++++++++++++++ tests/ui/crashes/ice-11230.rs | 10 ++++++++++ tests/ui/crashes/ice-11230.stderr | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/ui/crashes/ice-11230.fixed create mode 100644 tests/ui/crashes/ice-11230.stderr diff --git a/tests/ui/crashes/ice-11230.fixed b/tests/ui/crashes/ice-11230.fixed new file mode 100644 index 000000000000..1d4c3dd9dcc4 --- /dev/null +++ b/tests/ui/crashes/ice-11230.fixed @@ -0,0 +1,16 @@ +// Test for https://github.com/rust-lang/rust-clippy/issues/11230 +#![warn(clippy::explicit_iter_loop)] +#![warn(clippy::needless_collect)] + +// explicit_iter_loop +fn main() { + const A: &[for<'a> fn(&'a ())] = &[]; + for v in A {} +} + +// needless_collect +trait Helper<'a>: Iterator {} + +fn x(w: &mut dyn for<'a> Helper<'a>) { + w.next().is_none(); +} diff --git a/tests/ui/crashes/ice-11230.rs b/tests/ui/crashes/ice-11230.rs index 94044e9435ed..a16fb271497c 100644 --- a/tests/ui/crashes/ice-11230.rs +++ b/tests/ui/crashes/ice-11230.rs @@ -1,6 +1,16 @@ // Test for https://github.com/rust-lang/rust-clippy/issues/11230 +#![warn(clippy::explicit_iter_loop)] +#![warn(clippy::needless_collect)] +// explicit_iter_loop fn main() { const A: &[for<'a> fn(&'a ())] = &[]; for v in A.iter() {} } + +// needless_collect +trait Helper<'a>: Iterator {} + +fn x(w: &mut dyn for<'a> Helper<'a>) { + w.collect::>().is_empty(); +} diff --git a/tests/ui/crashes/ice-11230.stderr b/tests/ui/crashes/ice-11230.stderr new file mode 100644 index 000000000000..7167d90e456e --- /dev/null +++ b/tests/ui/crashes/ice-11230.stderr @@ -0,0 +1,20 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> tests/ui/crashes/ice-11230.rs:8:14 + | +LL | for v in A.iter() {} + | ^^^^^^^^ help: to write this more concisely, try: `A` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` + +error: avoid using `collect()` when not needed + --> tests/ui/crashes/ice-11230.rs:15:7 + | +LL | w.collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` + +error: aborting due to 2 previous errors + From 436e4fb647bca08bd5cb7eea6f8450bb56515c10 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Thu, 2 Jan 2025 13:10:11 +0100 Subject: [PATCH 005/342] Cast global variables to default address space Pointers for variables all need to be in the same address space for correct compilation. Therefore ensure that even if a global variable is created in a different address space, it is casted to the default address space before its value is used. This is necessary for the amdgpu target and others where the default address space for global variables is not 0. For example `core` does not compile in debug mode when not casting the address space to the default one because it tries to emit the following (simplified) LLVM IR, containing a type mismatch: ```llvm @alloc_0 = addrspace(1) constant <{ [6 x i8] }> <{ [6 x i8] c"bit.rs" }>, align 1 @alloc_1 = addrspace(1) constant <{ ptr }> <{ ptr addrspace(1) @alloc_0 }>, align 8 ; ^ here a struct containing a `ptr` is needed, but it is created using a `ptr addrspace(1)` ``` For this to compile, we need to insert a constant `addrspacecast` before we use a global variable: ```llvm @alloc_0 = addrspace(1) constant <{ [6 x i8] }> <{ [6 x i8] c"bit.rs" }>, align 1 @alloc_1 = addrspace(1) constant <{ ptr }> <{ ptr addrspacecast (ptr addrspace(1) @alloc_0 to ptr) }>, align 8 ``` As vtables are global variables as well, they are also created with an `addrspacecast`. In the SSA backend, after a vtable global is created, metadata is added to it. To add metadata, we need the non-casted global variable. Therefore we strip away an addrspacecast if there is one, to get the underlying global. --- compiler/rustc_codegen_llvm/src/builder.rs | 4 +- compiler/rustc_codegen_llvm/src/common.rs | 7 +- compiler/rustc_codegen_llvm/src/consts.rs | 48 ++++++++---- .../src/debuginfo/metadata.rs | 15 ++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 77 +++++++++++++++++++ 5 files changed, 130 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index ab0e43e60a4f..1b99fea8d515 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1225,7 +1225,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { fn get_static(&mut self, def_id: DefId) -> &'ll Value { // Forward to the `get_static` method of `CodegenCx` - self.cx().get_static(def_id) + let s = self.cx().get_static(def_id); + // Cast to default address space if statics are in a different addrspace + self.cx().const_pointercast(s, self.type_ptr()) } } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index adfe8aeb5c53..bd792c02810c 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -221,6 +221,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); } llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + let g = self.const_pointercast(g, self.type_ptr()); (s.to_owned(), g) }) .1; @@ -285,7 +286,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), - _ => self.static_addr_of(init, alloc.align, None), + _ => self.static_addr_of_impl(init, alloc.align, None), }; if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() { @@ -311,7 +312,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) .unwrap_memory(); let init = const_alloc_to_llvm(self, alloc, /*static*/ false); - let value = self.static_addr_of(init, alloc.inner().align, None); + let value = self.static_addr_of_impl(init, alloc.inner().align, None); (value, AddressSpace::DATA) } GlobalAlloc::Static(def_id) => { @@ -323,7 +324,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let llval = unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), - self.const_bitcast(base_addr, self.type_ptr_ext(base_addr_space)), + self.const_pointercast(base_addr, self.type_ptr_ext(base_addr_space)), &self.const_usize(offset.bytes()), 1, ) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index c7114480d8bd..b88fd133e8e8 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -210,6 +210,10 @@ impl<'ll> CodegenCx<'ll, '_> { unsafe { llvm::LLVMConstBitCast(val, ty) } } + pub(crate) fn const_pointercast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMConstPointerCast(val, ty) } + } + pub(crate) fn static_addr_of_mut( &self, cv: &'ll Value, @@ -233,6 +237,31 @@ impl<'ll> CodegenCx<'ll, '_> { gv } + pub(crate) fn static_addr_of_impl( + &self, + cv: &'ll Value, + align: Align, + kind: Option<&str>, + ) -> &'ll Value { + if let Some(&gv) = self.const_globals.borrow().get(&cv) { + unsafe { + // Upgrade the alignment in cases where the same constant is used with different + // alignment requirements + let llalign = align.bytes() as u32; + if llalign > llvm::LLVMGetAlignment(gv) { + llvm::LLVMSetAlignment(gv, llalign); + } + } + return gv; + } + let gv = self.static_addr_of_mut(cv, align, kind); + unsafe { + llvm::LLVMSetGlobalConstant(gv, True); + } + self.const_globals.borrow_mut().insert(cv, gv); + gv + } + #[instrument(level = "debug", skip(self))] pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value { let instance = Instance::mono(self.tcx, def_id); @@ -506,23 +535,8 @@ impl<'ll> CodegenCx<'ll, '_> { impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value { - if let Some(&gv) = self.const_globals.borrow().get(&cv) { - unsafe { - // Upgrade the alignment in cases where the same constant is used with different - // alignment requirements - let llalign = align.bytes() as u32; - if llalign > llvm::LLVMGetAlignment(gv) { - llvm::LLVMSetAlignment(gv, llalign); - } - } - return gv; - } - let gv = self.static_addr_of_mut(cv, align, kind); - unsafe { - llvm::LLVMSetGlobalConstant(gv, True); - } - self.const_globals.borrow_mut().insert(cv, gv); - gv + let gv = self.static_addr_of_impl(cv, align, kind); + self.const_pointercast(gv, self.type_ptr()) } fn codegen_static(&self, def_id: DefId) { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 40248a9009ae..e4da540da5cd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1500,6 +1500,18 @@ fn build_vtable_type_di_node<'ll, 'tcx>( .di_node } +fn find_vtable_behind_cast<'ll>(vtable: &'ll Value) -> &'ll Value { + // The vtable is a global variable, which may be behind an addrspacecast. + unsafe { + if let Some(c) = llvm::LLVMIsAConstantExpr(vtable) { + if llvm::LLVMGetConstOpcode(c) == llvm::Opcode::AddrSpaceCast { + return llvm::LLVMGetOperand(c, 0).unwrap(); + } + } + } + vtable +} + pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, @@ -1520,6 +1532,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let Some(trait_ref) = trait_ref else { return }; + let vtable = find_vtable_behind_cast(vtable); let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty); let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); let trait_def_id = trait_ref_self.def_id(); @@ -1593,6 +1606,8 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>( return; } + let vtable = find_vtable_behind_cast(vtable); + // When full debuginfo is enabled, we want to try and prevent vtables from being // merged. Otherwise debuggers will have a hard time mapping from dyn pointer // to concrete type. diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 472d4a3a72b3..8ae9c1e50170 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -660,6 +660,79 @@ pub enum MemoryEffects { InaccessibleMemOnly, } +/// LLVMOpcode +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub enum Opcode { + Ret = 1, + Br = 2, + Switch = 3, + IndirectBr = 4, + Invoke = 5, + Unreachable = 7, + CallBr = 67, + FNeg = 66, + Add = 8, + FAdd = 9, + Sub = 10, + FSub = 11, + Mul = 12, + FMul = 13, + UDiv = 14, + SDiv = 15, + FDiv = 16, + URem = 17, + SRem = 18, + FRem = 19, + Shl = 20, + LShr = 21, + AShr = 22, + And = 23, + Or = 24, + Xor = 25, + Alloca = 26, + Load = 27, + Store = 28, + GetElementPtr = 29, + Trunc = 30, + ZExt = 31, + SExt = 32, + FPToUI = 33, + FPToSI = 34, + UIToFP = 35, + SIToFP = 36, + FPTrunc = 37, + FPExt = 38, + PtrToInt = 39, + IntToPtr = 40, + BitCast = 41, + AddrSpaceCast = 60, + ICmp = 42, + FCmp = 43, + PHI = 44, + Call = 45, + Select = 46, + UserOp1 = 47, + UserOp2 = 48, + VAArg = 49, + ExtractElement = 50, + InsertElement = 51, + ShuffleVector = 52, + ExtractValue = 53, + InsertValue = 54, + Freeze = 68, + Fence = 55, + AtomicCmpXchg = 56, + AtomicRMW = 57, + Resume = 58, + LandingPad = 59, + CleanupRet = 61, + CatchRet = 62, + CatchPad = 63, + CleanupPad = 64, + CatchSwitch = 65, +} + unsafe extern "C" { type Opaque; } @@ -975,7 +1048,10 @@ unsafe extern "C" { pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + pub fn LLVMConstPointerCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub fn LLVMGetAggregateElement(ConstantVal: &Value, Idx: c_uint) -> Option<&Value>; + pub fn LLVMGetConstOpcode(ConstantVal: &Value) -> Opcode; + pub fn LLVMIsAConstantExpr(Val: &Value) -> Option<&Value>; // Operations on global variables, functions, and aliases (globals) pub fn LLVMIsDeclaration(Global: &Value) -> Bool; @@ -1032,6 +1108,7 @@ unsafe extern "C" { // Operations on instructions pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; pub fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; + pub fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>; // Operations on call sites pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint); From a7c2e4d750a8448f6623b6554346ea09db82aff2 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Thu, 2 Jan 2025 22:31:36 +0500 Subject: [PATCH 006/342] Fix link to release tags --- book/src/development/infrastructure/release.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/infrastructure/release.md b/book/src/development/infrastructure/release.md index 20b870eb69a8..8b080c099b81 100644 --- a/book/src/development/infrastructure/release.md +++ b/book/src/development/infrastructure/release.md @@ -96,9 +96,9 @@ git tag rust-1.XX.0 # XX should be exchanged with the correspondin git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote ``` -After this, the release should be available on the Clippy [release page]. +After this, the release should be available on the Clippy [tags page]. -[release page]: https://github.com/rust-lang/rust-clippy/releases +[tags page]: https://github.com/rust-lang/rust-clippy/tags ## Publish `clippy_utils` From 05cb1ce0ef6ba8720896aea64c11408d6721104c Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Fri, 3 Jan 2025 15:01:58 +0500 Subject: [PATCH 007/342] Fix not found links at Book --- book/src/development/common_tools_writing_lints.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index c354e8914f5d..b44ad80a25cc 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -265,10 +265,10 @@ functions to deal with macros: ``` [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html -[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.pat_ty [paths]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/paths/index.html From 39269aaaae12e3f50265b0a4cd17ecd90831b910 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Sun, 5 Jan 2025 20:22:15 +0900 Subject: [PATCH 008/342] auto-fix slow_vector_initialization --- .../src/slow_vector_initialization.rs | 11 ++- tests/ui/slow_vector_initialization.fixed | 85 +++++++++++++++++++ tests/ui/slow_vector_initialization.rs | 2 +- 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 tests/ui/slow_vector_initialization.fixed diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index d2d693eaa1f3..cdf538fce5c7 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -3,6 +3,7 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, + span_contains_comment, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -206,6 +207,14 @@ impl SlowVectorInit { let span_to_replace = slow_fill .span .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + + // If there is no comment in `span_to_replace`, Clippy can automatically fix the code. + let app = if span_contains_comment(cx.tcx.sess.source_map(), span_to_replace) { + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + span_lint_and_sugg( cx, SLOW_VECTOR_INITIALIZATION, @@ -213,7 +222,7 @@ impl SlowVectorInit { msg, "consider replacing this with", format!("vec![0; {len_expr}]"), - Applicability::Unspecified, + app, ); } } diff --git a/tests/ui/slow_vector_initialization.fixed b/tests/ui/slow_vector_initialization.fixed new file mode 100644 index 000000000000..8c16bb307ca0 --- /dev/null +++ b/tests/ui/slow_vector_initialization.fixed @@ -0,0 +1,85 @@ +#![allow(clippy::useless_vec)] +use std::iter::repeat; +fn main() { + resize_vector(); + extend_vector(); + mixed_extend_resize_vector(); + from_empty_vec(); +} + +fn extend_vector() { + // Extend with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Extend with len expression + let mut vec2 = vec![0; len - 10]; + + // Extend with mismatching expression should not be warned + let mut vec3 = Vec::with_capacity(24322); + vec3.extend(repeat(0).take(2)); + + let mut vec4 = vec![0; len]; +} + +fn mixed_extend_resize_vector() { + // Mismatching len + let mut mismatching_len = Vec::with_capacity(30); + mismatching_len.extend(repeat(0).take(40)); + + // Slow initialization + let mut resized_vec = vec![0; 30]; + + let mut extend_vec = vec![0; 30]; +} + +fn resize_vector() { + // Resize with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Resize mismatch len + let mut vec2 = Vec::with_capacity(200); + vec2.resize(10, 0); + + // Resize with len expression + let mut vec3 = vec![0; len - 10]; + + let mut vec4 = vec![0; len]; + + // Reinitialization should be warned + vec1 = vec![0; 10]; +} + +fn from_empty_vec() { + // Resize with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Resize with len expression + let mut vec3 = vec![0; len - 10]; + + // Reinitialization should be warned + vec1 = vec![0; 10]; + + vec1 = vec![0; 10]; + + macro_rules! x { + () => { + vec![] + }; + } + + // `vec![]` comes from another macro, don't warn + vec1 = x!(); + vec1.resize(10, 0); +} + +fn do_stuff(vec: &mut [u8]) {} + +fn extend_vector_with_manipulations_between() { + let len = 300; + let mut vec1: Vec = Vec::with_capacity(len); + do_stuff(&mut vec1); + vec1.extend(repeat(0).take(len)); +} diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 2ba87f412500..6831dad70b43 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -1,4 +1,4 @@ -//@no-rustfix +#![allow(clippy::useless_vec)] use std::iter::repeat; fn main() { resize_vector(); From c9315bc3953fcf15154df21f788f2f7a5e8d6e7d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 5 Jan 2025 22:09:00 +0100 Subject: [PATCH 009/342] Use diagnostic item instead of path for `core::fmt::Debug` This removes the last call to `LateContext::match_def_path()` in Clippy's code. The `LateContext::match_def_path()` in the compiler sources was only kept for Clippy's usage. --- clippy_lints/src/missing_fields_in_debug.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index f28b431ab997..e9ec23b1efa6 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { // this prevents ICEs such as when self is a type parameter or a primitive type // (see #10887, #11063) && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res - && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug]) + && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id) // don't trigger if this impl was derived && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) && !item.span.from_expansion() From e0625f4dd57630bedadb86cf72a16fb948051e96 Mon Sep 17 00:00:00 2001 From: Aaron Ang <67321817+aaron-ang@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:43:06 -0800 Subject: [PATCH 010/342] fix(adding_lints): usage of early vs late lint pass --- book/src/development/adding_lints.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index c07568697d02..cbea6dc8e8af 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -301,8 +301,8 @@ either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. In short, the `EarlyLintPass` runs before type checking and [HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass` -has access to type information. Consider using the `LateLintPass` unless you need -something specific from the `EarlyLintPass`. +has access to type information. Consider using the `EarlyLintPass` unless you need +something specific from the `LateLintPass`. Since we don't need type information for checking the function name, we used `--pass=early` when running the new lint automation and all the imports were From 16ee0148d417603135d0838521f4899ff441489e Mon Sep 17 00:00:00 2001 From: Aaron Ang <67321817+aaron-ang@users.noreply.github.com> Date: Mon, 6 Jan 2025 20:47:52 -0800 Subject: [PATCH 011/342] Update adding_lints.md --- book/src/development/adding_lints.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index cbea6dc8e8af..e502d525cf63 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -299,10 +299,11 @@ This is good, because it makes writing this particular lint less complicated. We have to make this decision with every new Clippy lint. It boils down to using either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. -In short, the `EarlyLintPass` runs before type checking and -[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass` -has access to type information. Consider using the `EarlyLintPass` unless you need -something specific from the `LateLintPass`. +`EarlyLintPass` runs before type checking and +[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering, while `LateLintPass` +runs after these stages, providing access to type information. The `cargo dev new_lint` command +defaults to the recommended `LateLintPass`, but you can specify `--pass=early` if your lint +only needs AST level analysis. Since we don't need type information for checking the function name, we used `--pass=early` when running the new lint automation and all the imports were From f1d909034ed2c585097a6180fa0e2061887a8434 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 7 Jan 2025 19:32:26 +0900 Subject: [PATCH 012/342] define_clippy_lints does not exist, declare_clippy_lints exists --- book/src/development/defining_lints.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/development/defining_lints.md b/book/src/development/defining_lints.md index ceabb255e2d0..169cecd7d11d 100644 --- a/book/src/development/defining_lints.md +++ b/book/src/development/defining_lints.md @@ -139,10 +139,10 @@ Untracked files: ``` -## The `define_clippy_lints` macro +## The `declare_clippy_lint` macro After `cargo dev new_lint`, you should see a macro with the name -`define_clippy_lints`. It will be in the same file if you defined a standalone +`declare_clippy_lint`. It will be in the same file if you defined a standalone lint, and it will be in `mod.rs` if you defined a type-specific lint. The macro looks something like this: From 9ad75b327a4daa830822f7ca0fa2885a1fe26a89 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 31 Oct 2024 11:39:45 +0100 Subject: [PATCH 013/342] Update tests. --- tests/ui/boxed_local.rs | 1 + tests/ui/boxed_local.stderr | 4 ++-- tests/ui/doc/doc-fixable.fixed | 2 +- tests/ui/doc/doc-fixable.rs | 2 +- .../ui/missing_const_for_fn/could_be_const.fixed | 1 + tests/ui/missing_const_for_fn/could_be_const.rs | 1 + .../missing_const_for_fn/could_be_const.stderr | 16 ++++++++-------- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/ui/boxed_local.rs b/tests/ui/boxed_local.rs index e2c27e585fce..4f361f5162be 100644 --- a/tests/ui/boxed_local.rs +++ b/tests/ui/boxed_local.rs @@ -173,6 +173,7 @@ mod issue_3739 { /// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} +#[allow(missing_abi)] #[rustfmt::skip] // Forces rustfmt to not add ABI pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} diff --git a/tests/ui/boxed_local.stderr b/tests/ui/boxed_local.stderr index d3156c820b2c..08fe375afb23 100644 --- a/tests/ui/boxed_local.stderr +++ b/tests/ui/boxed_local.stderr @@ -14,13 +14,13 @@ LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:188:44 + --> tests/ui/boxed_local.rs:189:44 | LL | fn default_impl_x(self: Box, x: Box) -> u32 { | ^ error: local variable doesn't need to be boxed here - --> tests/ui/boxed_local.rs:196:16 + --> tests/ui/boxed_local.rs:197:16 | LL | fn foo(x: Box) {} | ^ diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 5cf5c608a85c..83574a5cd98d 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -240,7 +240,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern { +extern "C" { /// `foo()` fn in_extern(); } diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 420211c65390..20fe89cdc536 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -240,7 +240,7 @@ fn parenthesized_word() {} /// UXes fn plural_acronym_test() {} -extern { +extern "C" { /// foo() fn in_extern(); } diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 014fbb85c7a3..dd9dedcdd044 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -149,6 +149,7 @@ mod msrv { //~^ ERROR: this could be a `const fn` #[rustfmt::skip] + #[allow(missing_abi)] const extern fn implicit_c() {} //~^ ERROR: this could be a `const fn` diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 4f7c2cbcf0b4..f974478540cd 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -149,6 +149,7 @@ mod msrv { //~^ ERROR: this could be a `const fn` #[rustfmt::skip] + #[allow(missing_abi)] extern fn implicit_c() {} //~^ ERROR: this could be a `const fn` diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index cc7dfd0888d0..33836bdfe9f8 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -222,7 +222,7 @@ LL | const extern "C" fn c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:153:9 | LL | extern fn implicit_c() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | const extern fn implicit_c() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:169:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:170:9 | LL | / pub fn new(strings: Vec) -> Self { LL | | Self { strings } @@ -246,7 +246,7 @@ LL | pub const fn new(strings: Vec) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:174:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:175:9 | LL | / pub fn empty() -> Self { LL | | Self { strings: Vec::new() } @@ -259,7 +259,7 @@ LL | pub const fn empty() -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:185:9 + --> tests/ui/missing_const_for_fn/could_be_const.rs:186:9 | LL | / pub fn new(text: String) -> Self { LL | | let vec = Vec::new(); @@ -273,7 +273,7 @@ LL | pub const fn new(text: String) -> Self { | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:204:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5 | LL | fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL | const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:208:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:209:5 | LL | extern "C-unwind" fn c_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -295,7 +295,7 @@ LL | const extern "C-unwind" fn c_unwind() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:210:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:211:5 | LL | extern "system" fn system() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -306,7 +306,7 @@ LL | const extern "system" fn system() {} | +++++ error: this could be a `const fn` - --> tests/ui/missing_const_for_fn/could_be_const.rs:212:5 + --> tests/ui/missing_const_for_fn/could_be_const.rs:213:5 | LL | extern "system-unwind" fn system_unwind() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 28d2363de8357150b5391e3bef147381e2e74437 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 11 Dec 2024 16:50:45 +0000 Subject: [PATCH 014/342] Exhaustively handle expressions in patterns --- clippy_lints/src/default_numeric_fallback.rs | 23 +++++++- clippy_lints/src/len_zero.rs | 12 +++- clippy_lints/src/manual_is_ascii_check.rs | 37 ++++++++---- clippy_lints/src/manual_range_patterns.rs | 13 ++-- clippy_lints/src/matches/match_bool.rs | 4 +- clippy_lints/src/matches/match_same_arms.rs | 8 +-- .../src/matches/match_str_case_mismatch.rs | 6 +- clippy_lints/src/matches/needless_match.rs | 12 ++-- clippy_lints/src/matches/overlapping_arms.rs | 8 ++- .../src/matches/redundant_pattern_match.rs | 4 +- clippy_lints/src/matches/single_match.rs | 6 +- clippy_lints/src/string_patterns.rs | 4 +- clippy_lints/src/utils/author.rs | 29 +++++++-- clippy_utils/src/consts.rs | 59 ++++++++++++------- clippy_utils/src/hir_utils.rs | 46 ++++++++++++--- tests/ui/author/if.stdout | 2 +- tests/ui/author/loop.stdout | 2 +- tests/ui/author/matches.stdout | 4 +- tests/ui/author/struct.stdout | 4 +- 19 files changed, 198 insertions(+), 85 deletions(-) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3b3a78cb1153..f579c9a41828 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -3,8 +3,11 @@ use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt}; -use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt}; +use rustc_hir::{ + Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Pat, PatExpr, PatExprKind, PatKind, Stmt, StmtKind, + StructTailExpr, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; @@ -219,6 +222,22 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { walk_expr(self, expr); } + fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { + match pat.kind { + PatKind::Lit(&PatExpr { + hir_id, + kind: PatExprKind::Lit { lit, .. }, + .. + }) => { + let ty = self.cx.typeck_results().node_type(hir_id); + self.check_lit(lit, ty, hir_id); + return; + }, + _ => {}, + } + walk_pat(self, pat) + } + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 3ea758e176f0..d6c1cb5e6e43 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -8,8 +8,8 @@ use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::{ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, - ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath, - TraitItemRef, TyKind, + ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, + QPath, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -163,7 +163,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { PatKind::Slice([], None, []) => true, - PatKind::Lit(lit) if is_empty_string(lit) => true, + PatKind::Lit(lit) => match lit.kind { + PatExprKind::Lit { lit, .. } => match lit.node { + LitKind::Str(lit, _) => lit.as_str().is_empty(), + _ => false, + }, + _ => false, + }, _ => false, } && !expr.span.from_expansion() diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 3f01f3cf30ae..5b9eedd9b8bf 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; +use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { { let arg = peel_ref_operators(cx, arg); let ty_sugg = get_ty_sugg(cx, arg, start); - let range = check_range(start, end); + let range = check_expr_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } } @@ -196,19 +196,34 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { } } -fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { +fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { if let ExprKind::Lit(start_lit) = &start.kind && let ExprKind::Lit(end_lit) = &end.kind { - match (&start_lit.node, &end_lit.node) { - (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, - (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, - (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, - (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, - (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, - _ => CharRange::Otherwise, - } + check_lit_range(start_lit, end_lit) } else { CharRange::Otherwise } } + + +fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { + if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind + && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind + { + check_lit_range(start_lit, end_lit) + } else { + CharRange::Otherwise + } +} + +fn check_lit_range(start_lit: &Lit, end_lit: &Lit) -> CharRange { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter, + (Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } +} diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index ffa3eacf3544..db65075e34fd 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; +use rustc_hir::{PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -38,14 +38,13 @@ declare_clippy_lint! { } declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]); -fn expr_as_i128(expr: &Expr<'_>) -> Option { - if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind { - expr_as_i128(expr).map(|num| -num) - } else if let ExprKind::Lit(lit) = expr.kind +fn expr_as_i128(expr: &PatExpr<'_>) -> Option { + if let PatExprKind::Lit { lit, negated } = expr.kind && let LitKind::Int(num, _) = lit.node { // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now. - num.get().try_into().ok() + let n = i128::try_from(num.get()).ok()?; + Some(if negated { -n } else { n }) } else { None } @@ -58,7 +57,7 @@ struct Num { } impl Num { - fn new(expr: &Expr<'_>) -> Option { + fn new(expr: &PatExpr<'_>) -> Option { Some(Self { val: expr_as_i128(expr)?, span: expr.span, diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index 69105ff0d5c7..c910653ed7f8 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet}; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -22,7 +22,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] if arms.len() == 2 { // no guards let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { - if let ExprKind::Lit(lit) = arm_bool.kind { + if let PatExprKind::Lit { lit, .. } = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 4b731d759723..ba294bcae699 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, PatExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -313,7 +313,7 @@ impl<'a> NormalizedPat<'a> { }, PatKind::Lit(e) => match &e.kind { // TODO: Handle negative integers. They're currently treated as a wild match. - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit{ lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), @@ -330,7 +330,7 @@ impl<'a> NormalizedPat<'a> { let start = match start { None => 0, Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => val.get(), LitKind::Char(val) => val.into(), LitKind::Byte(val) => val.into(), @@ -342,7 +342,7 @@ impl<'a> NormalizedPat<'a> { let (end, bounds) = match end { None => (u128::MAX, RangeEnd::Included), Some(e) => match &e.kind { - ExprKind::Lit(lit) => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Int(val, _) => (val.get(), bounds), LitKind::Char(val) => (val.into(), bounds), LitKind::Byte(val) => (val.into(), bounds), diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 1267fc9d0a53..db4c22fcda31 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if let PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + if let PatKind::Lit(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) = arm.pat.kind && let LitKind::Str(symbol, _) = lit.node diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 63bea586caf6..ddf2c1cc89b7 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { - if let ItemKind::Fn{ .. } = item.kind { + if let ItemKind::Fn { .. } = item.kind { let output = cx .tcx .fn_sig(item.owner_id) @@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }); }, // Example: `5 => 5` - (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => { - if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind { + (PatKind::Lit(pat_expr_expr), ExprKind::Lit(expr_spanned)) => { + if let PatExprKind::Lit { + lit: pat_spanned, + negated: false, + } = &pat_expr_expr.kind + { return pat_spanned.node == expr_spanned.node; } }, diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 856311899f26..ba677b423e27 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs_const = if let Some(lhs) = lhs { - ConstEvalCtxt::new(cx).eval(lhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { let min_val_const = ty.numeric_min_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? }; let rhs_const = if let Some(rhs) = rhs { - ConstEvalCtxt::new(cx).eval(rhs)? + ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { let max_val_const = ty.numeric_max_val(cx.tcx)?; mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? @@ -58,7 +58,9 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) } if let PatKind::Lit(value) = pat.kind { - let value = ConstEvalCtxt::new(cx).eval_full_int(value)?; + let value = ConstEvalCtxt::new(cx) + .eval_pat_expr(value)? + .int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?; return Some(SpannedRange { span: pat.span, node: (value, EndBound::Included(value)), diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 264458a86ef4..8243171a9355 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, PatExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -75,7 +75,7 @@ fn find_match_true<'tcx>( message: &'static str, ) { if let PatKind::Lit(lit) = pat.kind - && let ExprKind::Lit(lit) = lit.kind + && let PatExprKind::Lit{ lit, negated: false } = lit.kind && let LitKind::Bool(pat_is_true) = lit.node { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 10ca6832d9c1..1edd42969d27 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind, PatExpr, PatExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{Span, sym}; @@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { // string literals are already a reference. - PatKind::Lit(Expr { - kind: ExprKind::Lit(lit), + PatKind::Lit(PatExpr { + kind: PatExprKind::Lit { lit, negated: false }, .. }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, _ => pat_ref_count, diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index 0d85b1b858a4..048b67ed2762 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< return ControlFlow::Break(()); } if arm.pat.walk_short(|pat| match pat.kind { - PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => { + PatKind::Lit(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => { if let LitKind::Char(_) = lit.node { set_char_spans.push(lit.span); } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index c2dcb5ae1f9e..c92d5c0730c5 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, - FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, StructTailExpr, TyKind, + FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.expr(expr); } + fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) { + let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + match lit.value.kind { + PatExprKind::Lit { lit, negated } => { + bind!(self, lit); + bind!(self, negated); + kind!("Lit{{ref {lit}, {negated} }}"); + self.lit(lit); + }, + PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), + PatExprKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + }, + } + } + fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { @@ -721,13 +742,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { PatKind::Lit(lit_expr) => { bind!(self, lit_expr); kind!("Lit({lit_expr})"); - self.expr(lit_expr); + self.pat_expr(lit_expr); }, PatKind::Range(start, end, end_kind) => { opt_bind!(self, start, end); kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.expr(e)); - end.if_some(|e| self.expr(e)); + start.if_some(|e| self.pat_expr(e)); + end.if_some(|e| self.pat_expr(e)); }, PatKind::Slice(start, middle, end) => { bind!(self, start, end); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 43ddf06730dd..d46beddf7312 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -4,7 +4,6 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] -use crate::macros::HirNode; use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; @@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } + pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option> { + match &pat_expr.kind { + PatExprKind::Lit { lit, negated } => { + let ty = self.typeck.node_type_opt(pat_expr.hir_id); + let val = lit_to_mir_constant(&lit.node, ty); + if *negated { + self.constant_negate(&val, ty?) + } else { + Some(val) + } + } + PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), + } + } + + fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option> { + let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() { + self.tcx.crate_name(def_id.krate) == sym::core + } else { + false + }; + self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| { + let result = mir_to_const(self_.tcx, result)?; + // If source is already Constant we wouldn't want to override it with CoreConstant + self_.source.set( + if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { + ConstantSource::CoreConstant + } else { + ConstantSource::Constant + }, + ); + Some(result) + }) + } + /// Simple constant folding: Insert an expression, get a constant or none. fn expr(&self, e: &Expr<'_>) -> Option> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => { - let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() { - self.tcx.crate_name(def_id.krate) == sym::core - } else { - false - }; - self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| { - let result = mir_to_const(self_.tcx, result)?; - // If source is already Constant we wouldn't want to override it with CoreConstant - self_.source.set( - if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) { - ConstantSource::CoreConstant - } else { - ConstantSource::Constant - }, - ); - Some(result) - }) - }, + ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id), ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 7c4e834f8416..b3b81412d08c 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, - Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, - TyKind, + Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, + TraitBoundModifiers, Ty, TyKind, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; @@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> { li.name == ri.name && self.eq_pat(lp, rp) } + fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool { + match (&left.kind, &right.kind) { + ( + &PatExprKind::Lit { + lit: left, + negated: left_neg, + }, + &PatExprKind::Lit { + lit: right, + negated: right_neg, + }, + ) => left_neg == right_neg && left.node == right.node, + (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body), + (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right), + (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false, + } + } + /// Checks whether two patterns are the same. fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { @@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), - (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), + (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { - both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b)) - && both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b)) + both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) + && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), @@ -1073,6 +1091,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } + pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) { + std::mem::discriminant(&lit.kind).hash(&mut self.s); + match &lit.kind { + PatExprKind::Lit { lit, negated } => { + lit.node.hash(&mut self.s); + negated.hash(&mut self.s); + }, + PatExprKind::ConstBlock(c) => self.hash_body(c.body), + PatExprKind::Path(qpath) => self.hash_qpath(qpath), + } + } + pub fn hash_pat(&mut self, pat: &Pat<'_>) { std::mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { @@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), - PatKind::Lit(expr) => self.hash_expr(expr), + PatKind::Lit(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { for pat in pats { self.hash_pat(pat); @@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { - self.hash_expr(s); + self.hash_pat_expr(s); } if let Some(e) = e { - self.hash_expr(e); + self.hash_pat_expr(e); } std::mem::discriminant(&i).hash(&mut self.s); }, diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index a85dcddd3315..a9b4c540377f 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -31,7 +31,7 @@ if let StmtKind::Let(local) = stmt.kind if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind && let PatKind::Lit(lit_expr) = let_expr.pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.init.kind && match_qpath(qpath, &["a"]) diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index 609d24910610..c33e0d2cdee0 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -77,7 +77,7 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) && let PatKind::Lit(lit_expr) = let_pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.kind && match_qpath(qpath, &["a"]) diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 91b3b6f6877e..6c08110a3f1c 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 && let PatKind::Lit(lit_expr) = arms[0].pat.kind - && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node && let PatKind::Lit(lit_expr1) = arms[1].pat.kind - && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() && let ExprKind::Block(block, None) = arms[1].body.kind diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index 0b332d5e7d0e..46f68cb79b04 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -24,7 +24,7 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind && fields.len() == 1 && fields[0].ident.as_str() == "field" && let PatKind::Lit(lit_expr) = fields[0].pat.kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind @@ -37,7 +37,7 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 && let PatKind::Lit(lit_expr) = fields[0].kind - && let ExprKind::Lit(ref lit) = lit_expr.kind + && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() && let ExprKind::Block(block, None) = arm.body.kind From 0faf8c7c62377984a6c710c329852fdc2d09824e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 7 Jan 2025 08:56:23 +0000 Subject: [PATCH 015/342] Rename PatKind::Lit to Expr --- clippy_lints/src/default_numeric_fallback.rs | 2 +- clippy_lints/src/equatable_if_let.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/manual_range_patterns.rs | 2 +- clippy_lints/src/matches/match_bool.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 6 +++--- clippy_lints/src/matches/match_str_case_mismatch.rs | 2 +- clippy_lints/src/matches/needless_match.rs | 2 +- clippy_lints/src/matches/overlapping_arms.rs | 2 +- clippy_lints/src/matches/redundant_pattern_match.rs | 6 +++--- clippy_lints/src/matches/single_match.rs | 8 ++++---- clippy_lints/src/string_patterns.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 4 ++-- clippy_lints/src/utils/author.rs | 6 +++--- clippy_utils/src/ast_utils/mod.rs | 2 +- clippy_utils/src/hir_utils.rs | 4 ++-- clippy_utils/src/lib.rs | 2 +- tests/ui/author/if.stdout | 2 +- tests/ui/author/loop.stdout | 2 +- tests/ui/author/matches.stdout | 4 ++-- tests/ui/author/struct.stdout | 4 ++-- 21 files changed, 34 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index f579c9a41828..c04a73c890f0 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -224,7 +224,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { match pat.kind { - PatKind::Lit(&PatExpr { + PatKind::Expr(&PatExpr { hir_id, kind: PatExprKind::Lit { lit, .. }, .. diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 9c8edfd6113f..8a5cf7f56d5f 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), - PatKind::Path(_) | PatKind::Lit(_) => true, + PatKind::Path(_) | PatKind::Expr(_) => true, } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index d6c1cb5e6e43..1c63ca9d9743 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -163,7 +163,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let ExprKind::Let(lt) = expr.kind && match lt.pat.kind { PatKind::Slice([], None, []) => true, - PatKind::Lit(lit) => match lit.kind { + PatKind::Expr(lit) => match lit.kind { PatExprKind::Lit { lit, .. } => match lit.node { LitKind::Str(lit, _) => lit.as_str().is_empty(), _ => false, diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index db65075e34fd..2e5a92915d9c 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -89,7 +89,7 @@ impl LateLintPass<'_> for ManualRangePatterns { let mut ranges_found = Vec::new(); for pat in pats { - if let PatKind::Lit(lit) = pat.kind + if let PatKind::Expr(lit) = pat.kind && let Some(num) = Num::new(lit) { numbers_found.insert(num.val); diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index c910653ed7f8..7e43d222a662 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -21,7 +21,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { + let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { if let PatExprKind::Lit { lit, .. } = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index ba294bcae699..28e05c273d5c 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, PatExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -311,9 +311,9 @@ impl<'a> NormalizedPat<'a> { ); Self::Tuple(None, pats) }, - PatKind::Lit(e) => match &e.kind { + PatKind::Expr(e) => match &e.kind { // TODO: Handle negative integers. They're currently treated as a wild match. - PatExprKind::Lit{ lit, negated: false } => match lit.node { + PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index db4c22fcda31..9f5b7c855a13 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -85,7 +85,7 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<( }; for arm in arms { - if let PatKind::Lit(PatExpr { + if let PatKind::Expr(PatExpr { kind: PatExprKind::Lit { lit, negated: false }, .. }) = arm.pat.kind diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index ddf2c1cc89b7..0d5575efc220 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -189,7 +189,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { }); }, // Example: `5 => 5` - (PatKind::Lit(pat_expr_expr), ExprKind::Lit(expr_spanned)) => { + (PatKind::Expr(pat_expr_expr), ExprKind::Lit(expr_spanned)) => { if let PatExprKind::Lit { lit: pat_spanned, negated: false, diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index ba677b423e27..4a5d3c516b88 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -57,7 +57,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(value) = pat.kind { + if let PatKind::Expr(value) = pat.kind { let value = ConstEvalCtxt::new(cx) .eval_pat_expr(value)? .int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?; diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 8243171a9355..7e74b36b4415 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, PatExprKind}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -74,8 +74,8 @@ fn find_match_true<'tcx>( span: Span, message: &'static str, ) { - if let PatKind::Lit(lit) = pat.kind - && let PatExprKind::Lit{ lit, negated: false } = lit.kind + if let PatKind::Expr(lit) = pat.kind + && let PatExprKind::Lit { lit, negated: false } = lit.kind && let LitKind::Bool(pat_is_true) = lit.node { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 1edd42969d27..b1d0686ffc42 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -9,7 +9,7 @@ use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_pat}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind, PatExpr, PatExprKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef}; use rustc_span::{Span, sym}; @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind + let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -126,7 +126,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { // string literals are already a reference. - PatKind::Lit(PatExpr { + PatKind::Expr(PatExpr { kind: PatExprKind::Lit { lit, negated: false }, .. }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, @@ -384,7 +384,7 @@ impl<'a> PatState<'a> { PatKind::Wild | PatKind::Binding(_, _, _, None) - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(..) | PatKind::Path(_) | PatKind::Never diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index 048b67ed2762..3834087f7977 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< return ControlFlow::Break(()); } if arm.pat.walk_short(|pat| match pat.kind { - PatKind::Lit(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => { + PatKind::Expr(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => { if let LitKind::Char(_) = lit.node { set_char_spans.push(lit.span); } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 50a97579df77..8923484bb58f 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { - if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { // This is a leaf pattern, so cloning is unprofitable. return; } @@ -228,7 +228,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // Therefore they are not some form of constructor `C`, // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. - Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) + Ident(.., None) | Expr(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_) // Skip immutable refs, as grouping them saves few characters, // and almost always requires adding parens (increasing noisiness). // In the case of only two patterns, replacement adds net characters. diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index c92d5c0730c5..4dcc8ac7fb0a 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -738,10 +738,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Guard({pat}, {cond})"); self.pat(pat); self.expr(cond); - } - PatKind::Lit(lit_expr) => { + }, + PatKind::Expr(lit_expr) => { bind!(self, lit_expr); - kind!("Lit({lit_expr})"); + kind!("Expr({lit_expr})"); self.pat_expr(lit_expr); }, PatKind::Range(start, end, end_kind) => { diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 623d9c760861..2eb09bac8d88 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -36,7 +36,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, - (Lit(l), Lit(r)) => eq_expr(l, r), + (Expr(l), Expr(r)) => eq_expr(l, r), (Ident(b1, i1, s1), Ident(b2, i2, s2)) => { b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat) }, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b3b81412d08c..d1d0abd46909 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -525,7 +525,7 @@ impl HirEqInterExpr<'_, '_, '_> { eq }, (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), - (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_pat_expr(l, r), + (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b)) @@ -1114,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat), - PatKind::Lit(expr) => self.hash_pat_expr(expr), + PatKind::Expr(expr) => self.hash_pat_expr(expr), PatKind::Or(pats) => { for pat in pats { self.hash_pat(pat); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index ba4a0f44584e..d42e40acbc08 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, + PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true, } } diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index a9b4c540377f..8ffdf8862027 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -30,7 +30,7 @@ if let StmtKind::Let(local) = stmt.kind } if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind && let ExprKind::Let(let_expr) = cond.kind - && let PatKind::Lit(lit_expr) = let_expr.pat.kind + && let PatKind::Expr(lit_expr) = let_expr.pat.kind && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.init.kind diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index c33e0d2cdee0..c94eb171f52b 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -76,7 +76,7 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While: // report your lint here } if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) - && let PatKind::Lit(lit_expr) = let_pat.kind + && let PatKind::Expr(lit_expr) = let_pat.kind && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Bool(true) = lit.node && let ExprKind::Path(ref qpath) = let_expr.kind diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 6c08110a3f1c..acb3b140dfa1 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -4,13 +4,13 @@ if let StmtKind::Let(local) = stmt.kind && let ExprKind::Lit(ref lit) = scrutinee.kind && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node && arms.len() == 3 - && let PatKind::Lit(lit_expr) = arms[0].pat.kind + && let PatKind::Expr(lit_expr) = arms[0].pat.kind && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node && arms[0].guard.is_none() && let ExprKind::Lit(ref lit2) = arms[0].body.kind && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node - && let PatKind::Lit(lit_expr1) = arms[1].pat.kind + && let PatKind::Expr(lit_expr1) = arms[1].pat.kind && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node && arms[1].guard.is_none() diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index 46f68cb79b04..b66bbccb3cf1 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -23,7 +23,7 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind && match_qpath(qpath, &["Test"]) && fields.len() == 1 && fields[0].ident.as_str() == "field" - && let PatKind::Lit(lit_expr) = fields[0].pat.kind + && let PatKind::Expr(lit_expr) = fields[0].pat.kind && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() @@ -36,7 +36,7 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind && match_qpath(qpath, &["TestTuple"]) && fields.len() == 1 - && let PatKind::Lit(lit_expr) = fields[0].kind + && let PatKind::Expr(lit_expr) = fields[0].kind && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node && arm.guard.is_none() From c686ffd193fc6c745caf8bb46f935e2ef17ee264 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 2 Jan 2025 13:28:00 +0100 Subject: [PATCH 016/342] Do not propose to elide lifetimes if this causes an ambiguity Some lifetimes in function return types are not bound to concrete content and can be set arbitrarily. Clippy should not propose to replace them by the default `'_` lifetime if such a lifetime cannot be determined unambigously. --- clippy_lints/src/lifetimes.rs | 39 ++++++++++++++ tests/ui/needless_lifetimes.fixed | 81 ++++++++++++++++++++++++++++++ tests/ui/needless_lifetimes.rs | 81 ++++++++++++++++++++++++++++++ tests/ui/needless_lifetimes.stderr | 61 +++++++++++++++++++++- 4 files changed, 261 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 8b2eee34a972..f99bc259a495 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -482,11 +482,13 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } +#[allow(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, in_bounded_ty: bool, in_generics_arg: bool, + lifetime_elision_impossible: bool, } struct LifetimeChecker<'cx, 'tcx, F> { @@ -495,6 +497,7 @@ struct LifetimeChecker<'cx, 'tcx, F> { where_predicate_depth: usize, bounded_ty_depth: usize, generic_args_depth: usize, + lifetime_elision_impossible: bool, phantom: std::marker::PhantomData, } @@ -519,6 +522,7 @@ where where_predicate_depth: 0, bounded_ty_depth: 0, generic_args_depth: 0, + lifetime_elision_impossible: false, phantom: std::marker::PhantomData, } } @@ -560,6 +564,7 @@ where in_where_predicate: self.where_predicate_depth != 0, in_bounded_ty: self.bounded_ty_depth != 0, in_generics_arg: self.generic_args_depth != 0, + lifetime_elision_impossible: self.lifetime_elision_impossible, }); } } @@ -586,11 +591,44 @@ where self.generic_args_depth -= 1; } + fn visit_fn_decl(&mut self, fd: &'tcx FnDecl<'tcx>) -> Self::Result { + self.lifetime_elision_impossible = !is_candidate_for_elision(fd); + walk_fn_decl(self, fd); + self.lifetime_elision_impossible = false; + } + fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } } +/// Check if `fd` supports function elision with an anonymous (or elided) lifetime, +/// and has a lifetime somewhere in its output type. +fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool { + struct V; + + impl Visitor<'_> for V { + type Result = ControlFlow; + + fn visit_lifetime(&mut self, lifetime: &Lifetime) -> Self::Result { + ControlFlow::Break(lifetime.is_elided() || lifetime.is_anonymous()) + } + } + + if fd.lifetime_elision_allowed + && let Return(ret_ty) = fd.output + && walk_ty(&mut V, ret_ty).is_break() + { + // The first encountered input lifetime will either be one on `self`, or will be the only lifetime. + fd.inputs + .iter() + .find_map(|ty| walk_ty(&mut V, ty).break_value()) + .unwrap() + } else { + false + } +} + fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { let mut checker = LifetimeChecker::::new(cx, generics); @@ -656,6 +694,7 @@ fn report_elidable_impl_lifetimes<'tcx>( Usage { lifetime, in_where_predicate: false, + lifetime_elision_impossible: false, .. }, ] = usages.as_slice() diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index 8196d608abd2..77188254316a 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -576,4 +576,85 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +mod issue13923 { + struct Py<'py> { + data: &'py str, + } + + enum Content<'t, 'py> { + Py(Py<'py>), + T1(&'t str), + T2(&'t str), + } + + enum ContentString<'t> { + T1(&'t str), + T2(&'t str), + } + + impl<'t, 'py> ContentString<'t> { + // `'py` cannot be elided + fn map_content1(self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&self` + fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&'_ self` + fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` should not be elided as the default lifetime, even if working, could be named as `'t` + fn map_content4(self, f: impl FnOnce(&'t str) -> &'t str, o: &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&Self` + fn map_content5( + self: std::pin::Pin<&Self>, + f: impl FnOnce(&'t str) -> &'t str, + o: &'t str, + ) -> Content<'t, '_> { + match *self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + struct Cx<'a, 'b> { + a: &'a u32, + b: &'b u32, + } + + // `'c` cannot be elided because we have several input lifetimes + fn one_explicit<'b>(x: Cx<'_, 'b>) -> &'b u32 { + x.b + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index b55dd99c46d0..c74121d0d7b8 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -576,4 +576,85 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +mod issue13923 { + struct Py<'py> { + data: &'py str, + } + + enum Content<'t, 'py> { + Py(Py<'py>), + T1(&'t str), + T2(&'t str), + } + + enum ContentString<'t> { + T1(&'t str), + T2(&'t str), + } + + impl<'t, 'py> ContentString<'t> { + // `'py` cannot be elided + fn map_content1(self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&self` + fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&'_ self` + fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` should not be elided as the default lifetime, even if working, could be named as `'t` + fn map_content4(self, f: impl FnOnce(&'t str) -> &'t str, o: &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&Self` + fn map_content5( + self: std::pin::Pin<&Self>, + f: impl FnOnce(&'t str) -> &'t str, + o: &'t str, + ) -> Content<'t, 'py> { + match *self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + struct Cx<'a, 'b> { + a: &'a u32, + b: &'b u32, + } + + // `'c` cannot be elided because we have several input lifetimes + fn one_explicit<'b>(x: Cx<'_, 'b>) -> &'b u32 { + &x.b + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index e56c914cc86d..5a739201d3d0 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -576,5 +576,64 @@ LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { LL + fn one_input(x: &u8) -> &u8 { | -error: aborting due to 48 previous errors +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:605:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +LL | // `'py` can be elided because of `&self` +LL | fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&self` +LL ~ fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + | + +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:615:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +LL | // `'py` can be elided because of `&'_ self` +LL | fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&'_ self` +LL ~ fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + | + +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:635:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +... +LL | ) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&Self` +... +LL | o: &'t str, +LL ~ ) -> Content<'t, '_> { + | + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_lifetimes.rs:656:9 + | +LL | &x.b + | ^^^^ help: change this to: `x.b` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` + +error: aborting due to 52 previous errors From 55c8550221bcf1c62d5dd73806c7a54f0dc4e24d Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 9 Jan 2025 18:24:02 +0100 Subject: [PATCH 017/342] Also deploy index.html file on release --- .github/deploy.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/deploy.sh b/.github/deploy.sh index ea118a3b6fce..2f062799a360 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -45,6 +45,8 @@ if [[ -n $TAG_NAME ]]; then git add "$TAG_NAME" # Update the symlink git add stable + # Update the index.html file + git add index.html git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then if git diff --exit-code --quiet -- beta/; then From e42e354f653f711fa4e69d73c1b25dc5a5c051c9 Mon Sep 17 00:00:00 2001 From: Quentin Santos Date: Thu, 9 Jan 2025 18:35:00 +0100 Subject: [PATCH 018/342] Remove needless check of returned type We are checking that we are calling the `as_bytes()` method of `String` or `str`. Checking that it returns a `slice()` does not add anything. --- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/methods/needless_as_bytes.rs | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 51351f6b7cd1..fa44bda83ee5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4925,7 +4925,7 @@ impl Methods { ("is_empty", []) => { match method_call(recv) { Some(("as_bytes", prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span); + needless_as_bytes::check(cx, "is_empty", prev_recv, expr.span); }, Some(("as_str", recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); @@ -4963,7 +4963,7 @@ impl Methods { }, ("len", []) => { if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span); + needless_as_bytes::check(cx, "len", prev_recv, expr.span); } }, ("lock", []) => { diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs index 75e9f3172303..451c7f746949 100644 --- a/clippy_lints/src/methods/needless_as_bytes.rs +++ b/clippy_lints/src/methods/needless_as_bytes.rs @@ -8,11 +8,9 @@ use rustc_span::Span; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) { - if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() - && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs() - && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str()) - { +pub fn check(cx: &LateContext<'_>, method: &str, prev_recv: &Expr<'_>, span: Span) { + let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); + if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( From d5264c7a4641abdfc8fd36d3e870d9ba49345180 Mon Sep 17 00:00:00 2001 From: Quentin Santos Date: Thu, 9 Jan 2025 18:38:10 +0100 Subject: [PATCH 019/342] Check for needless uses of str::bytes() This builds upon the lint for `str::as_bytes()`, and also covers needless uses of the iterator version `str::bytes()`. --- clippy_lints/src/methods/mod.rs | 8 ++-- clippy_lints/src/methods/needless_as_bytes.rs | 4 +- tests/ui/needless_as_bytes.fixed | 36 ++++++++++++++++ tests/ui/needless_as_bytes.rs | 36 ++++++++++++++++ tests/ui/needless_as_bytes.stderr | 42 +++++++++++++++---- 5 files changed, 111 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa44bda83ee5..f66466896e5b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4924,8 +4924,8 @@ impl Methods { }, ("is_empty", []) => { match method_call(recv) { - Some(("as_bytes", prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, "is_empty", prev_recv, expr.span); + Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span); }, Some(("as_str", recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); @@ -4962,8 +4962,8 @@ impl Methods { double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { - if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, "len", prev_recv, expr.span); + if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span); } }, ("lock", []) => { diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs index 451c7f746949..7c9f7bae9906 100644 --- a/clippy_lints/src/methods/needless_as_bytes.rs +++ b/clippy_lints/src/methods/needless_as_bytes.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, method: &str, prev_recv: &Expr<'_>, span: Span) { +pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) { let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; @@ -17,7 +17,7 @@ pub fn check(cx: &LateContext<'_>, method: &str, prev_recv: &Expr<'_>, span: Spa cx, NEEDLESS_AS_BYTES, span, - "needless call to `as_bytes()`", + format!("needless call to `{prev_method}`"), format!("`{method}()` can be called directly on strings"), format!("{sugg}.{method}()"), app, diff --git a/tests/ui/needless_as_bytes.fixed b/tests/ui/needless_as_bytes.fixed index 042342311fdf..74b4ba5be798 100644 --- a/tests/ui/needless_as_bytes.fixed +++ b/tests/ui/needless_as_bytes.fixed @@ -1,5 +1,6 @@ #![warn(clippy::needless_as_bytes)] #![allow(clippy::const_is_empty)] +#![feature(exact_size_is_empty)] struct S; @@ -7,6 +8,9 @@ impl S { fn as_bytes(&self) -> &[u8] { &[] } + fn bytes(&self) -> &[u8] { + &[] + } } fn main() { @@ -15,6 +19,11 @@ fn main() { println!("len = {}", "some string".len()); //~^ needless_as_bytes } + if "some string".is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".len()); + //~^ needless_as_bytes + } let s = String::from("yet another string"); if s.is_empty() { @@ -22,6 +31,11 @@ fn main() { println!("len = {}", s.len()); //~^ needless_as_bytes } + if s.is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.len()); + //~^ needless_as_bytes + } // Do not lint let _ = S.as_bytes().is_empty(); @@ -36,6 +50,18 @@ fn main() { }; } m!(1).as_bytes().len(); + let _ = S.bytes().is_empty(); + let _ = S.bytes().len(); + let _ = (&String::new() as &dyn Bytes).bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".bytes() + }; + } + m!(1).bytes().len(); m!(2).len(); } @@ -48,3 +74,13 @@ impl AsBytes for String { &[] } } + +pub trait Bytes { + fn bytes(&self) -> &[u8]; +} + +impl Bytes for String { + fn bytes(&self) -> &[u8] { + &[] + } +} diff --git a/tests/ui/needless_as_bytes.rs b/tests/ui/needless_as_bytes.rs index c481e041e0ab..ffcce60bbbef 100644 --- a/tests/ui/needless_as_bytes.rs +++ b/tests/ui/needless_as_bytes.rs @@ -1,5 +1,6 @@ #![warn(clippy::needless_as_bytes)] #![allow(clippy::const_is_empty)] +#![feature(exact_size_is_empty)] struct S; @@ -7,6 +8,9 @@ impl S { fn as_bytes(&self) -> &[u8] { &[] } + fn bytes(&self) -> &[u8] { + &[] + } } fn main() { @@ -15,6 +19,11 @@ fn main() { println!("len = {}", "some string".as_bytes().len()); //~^ needless_as_bytes } + if "some string".bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".bytes().len()); + //~^ needless_as_bytes + } let s = String::from("yet another string"); if s.as_bytes().is_empty() { @@ -22,6 +31,11 @@ fn main() { println!("len = {}", s.as_bytes().len()); //~^ needless_as_bytes } + if s.bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.bytes().len()); + //~^ needless_as_bytes + } // Do not lint let _ = S.as_bytes().is_empty(); @@ -36,6 +50,18 @@ fn main() { }; } m!(1).as_bytes().len(); + let _ = S.bytes().is_empty(); + let _ = S.bytes().len(); + let _ = (&String::new() as &dyn Bytes).bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".bytes() + }; + } + m!(1).bytes().len(); m!(2).len(); } @@ -48,3 +74,13 @@ impl AsBytes for String { &[] } } + +pub trait Bytes { + fn bytes(&self) -> &[u8]; +} + +impl Bytes for String { + fn bytes(&self) -> &[u8] { + &[] + } +} diff --git a/tests/ui/needless_as_bytes.stderr b/tests/ui/needless_as_bytes.stderr index 3391238a142b..138c6630ae7d 100644 --- a/tests/ui/needless_as_bytes.stderr +++ b/tests/ui/needless_as_bytes.stderr @@ -1,5 +1,5 @@ -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:13:8 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:17:8 | LL | if "some string".as_bytes().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` @@ -7,23 +7,47 @@ LL | if "some string".as_bytes().is_empty() { = note: `-D clippy::needless-as-bytes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:15:30 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:19:30 | LL | println!("len = {}", "some string".as_bytes().len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:20:8 +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:22:8 + | +LL | if "some string".bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` + +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:24:30 + | +LL | println!("len = {}", "some string".bytes().len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` + +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:29:8 | LL | if s.as_bytes().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:22:30 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:31:30 | LL | println!("len = {}", s.as_bytes().len()); | ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` -error: aborting due to 4 previous errors +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:34:8 + | +LL | if s.bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` + +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:36:30 + | +LL | println!("len = {}", s.bytes().len()); + | ^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` + +error: aborting due to 8 previous errors From d0a74af9794d077cea7ed8979071de122334e503 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 9 Jan 2025 18:57:00 +0100 Subject: [PATCH 020/342] Merge commit '19e305bb57a7595f2a8d81f521c0dd8bf854e739' into clippy-subtree-update --- .github/workflows/clippy_dev.yml | 3 + .github/workflows/clippy_mq.yml | 27 +++- .github/workflows/clippy_pr.yml | 7 +- .github/workflows/deploy.yml | 10 +- .github/workflows/lintcheck.yml | 8 + .github/workflows/remark.yml | 3 + CHANGELOG.md | 47 +++++- Cargo.toml | 2 +- .../infrastructure/changelog_update.md | 5 + book/src/development/method_checking.md | 2 +- book/src/lint_configuration.md | 27 ++++ clippy.toml | 2 + clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 20 +++ clippy_dev/src/fmt.rs | 6 +- clippy_lints/Cargo.toml | 2 +- .../src/arbitrary_source_item_ordering.rs | 4 +- .../src/attrs/mixed_attributes_style.rs | 2 +- clippy_lints/src/casts/borrow_as_ptr.rs | 29 ++-- clippy_lints/src/casts/mod.rs | 7 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc/missing_headers.rs | 11 +- clippy_lints/src/doc/mod.rs | 2 +- .../src/doc/too_long_first_doc_paragraph.rs | 2 +- clippy_lints/src/entry.rs | 4 +- clippy_lints/src/eta_reduction.rs | 2 +- clippy_lints/src/implied_bounds_in_impls.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 127 +++++++++++----- clippy_lints/src/len_zero.rs | 39 +++-- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/loops/infinite_loop.rs | 2 +- clippy_lints/src/macro_metavars_in_unsafe.rs | 6 +- clippy_lints/src/manual_div_ceil.rs | 38 ++++- clippy_lints/src/manual_ignore_case_cmp.rs | 2 +- clippy_lints/src/manual_is_ascii_check.rs | 26 ++-- clippy_lints/src/matches/mod.rs | 5 - .../src/matches/redundant_pattern_match.rs | 8 +- clippy_lints/src/matches/single_match.rs | 2 +- .../src/methods/double_ended_iterator_last.rs | 41 +++++ clippy_lints/src/methods/map_flatten.rs | 9 +- clippy_lints/src/methods/map_identity.rs | 15 +- clippy_lints/src/methods/mod.rs | 29 ++++ .../methods/needless_character_iteration.rs | 3 +- clippy_lints/src/methods/needless_collect.rs | 10 +- .../src/methods/read_line_without_trim.rs | 3 +- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_lints/src/methods/unnecessary_fold.rs | 8 +- .../src/methods/unnecessary_map_or.rs | 24 ++- .../src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 9 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- .../src/needless_arbitrary_self_type.rs | 7 +- clippy_lints/src/needless_continue.rs | 143 ++++++++++++++---- clippy_lints/src/no_effect.rs | 2 +- clippy_lints/src/non_copy_const.rs | 11 +- clippy_lints/src/non_expressive_names.rs | 2 +- .../src/operators/arithmetic_side_effects.rs | 2 +- clippy_lints/src/pathbuf_init_then_push.rs | 8 +- clippy_lints/src/redundant_else.rs | 1 - clippy_lints/src/redundant_locals.rs | 6 +- clippy_lints/src/regex.rs | 12 +- .../src/significant_drop_tightening.rs | 4 +- .../src/slow_vector_initialization.rs | 22 +-- clippy_lints/src/swap.rs | 4 +- clippy_lints/src/trailing_empty_array.rs | 7 +- .../src/transmute/transmute_undefined_repr.rs | 4 - clippy_lints/src/types/mod.rs | 2 +- clippy_lints/src/unnecessary_literal_bound.rs | 2 +- clippy_lints/src/unused_async.rs | 6 +- clippy_lints/src/unwrap.rs | 4 +- .../src/utils/internal_lints/invalid_paths.rs | 1 + clippy_lints/src/vec.rs | 16 +- clippy_lints/src/vec_init_then_push.rs | 4 +- clippy_lints/src/write.rs | 2 +- clippy_lints/src/zombie_processes.rs | 2 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/mir/possible_borrower.rs | 2 +- clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/paths.rs | 2 + lintcheck/src/output.rs | 2 +- rust-toolchain | 2 +- tests/compile-test.rs | 2 +- tests/missing-test-files.rs | 2 +- tests/ui-internal/custom_ice_message.stderr | 1 + .../clippy.toml | 1 + ...conf_inconsistent_struct_constructor.fixed | 79 ++++++++++ .../conf_inconsistent_struct_constructor.rs | 79 ++++++++++ ...onf_inconsistent_struct_constructor.stderr | 77 ++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/borrow_as_ptr.fixed | 8 + tests/ui/borrow_as_ptr.rs | 8 + tests/ui/borrow_as_ptr.stderr | 14 +- .../borrow_interior_mutable_const/others.rs | 11 ++ .../others.stderr | 28 ++-- tests/ui/crashes/ice-10972-tait.rs | 9 ++ tests/ui/crashes/ice-13862.rs | 19 +++ tests/ui/double_ended_iterator_last.fixed | 53 +++++++ tests/ui/double_ended_iterator_last.rs | 53 +++++++ tests/ui/double_ended_iterator_last.stderr | 17 +++ .../ui/inconsistent_struct_constructor.fixed | 6 +- .../ui/inconsistent_struct_constructor.stderr | 19 ++- tests/ui/infinite_iter.rs | 2 +- tests/ui/iter_overeager_cloned.fixed | 7 +- tests/ui/iter_overeager_cloned.rs | 7 +- tests/ui/iter_overeager_cloned.stderr | 38 ++--- tests/ui/len_zero.fixed | 22 +++ tests/ui/len_zero.rs | 22 +++ tests/ui/len_zero.stderr | 42 ++--- tests/ui/manual_div_ceil.fixed | 22 +++ tests/ui/manual_div_ceil.rs | 22 +++ tests/ui/manual_div_ceil.stderr | 56 ++++++- tests/ui/manual_div_ceil_with_feature.fixed | 27 ++++ tests/ui/manual_div_ceil_with_feature.rs | 27 ++++ tests/ui/manual_div_ceil_with_feature.stderr | 68 ++++++++- tests/ui/manual_is_ascii_check.fixed | 5 + tests/ui/manual_is_ascii_check.rs | 5 + tests/ui/manual_is_ascii_check.stderr | 24 ++- tests/ui/map_flatten.rs | 12 ++ tests/ui/map_flatten.stderr | 11 +- tests/ui/map_identity.fixed | 15 ++ tests/ui/map_identity.rs | 15 ++ tests/ui/map_identity.stderr | 14 +- .../ui/missing_const_for_fn/cant_be_const.rs | 29 +++- tests/ui/needless_arbitrary_self_type.fixed | 5 + tests/ui/needless_arbitrary_self_type.rs | 5 + tests/ui/needless_arbitrary_self_type.stderr | 14 +- tests/ui/needless_continue.rs | 65 ++++++++ tests/ui/needless_continue.stderr | 80 +++++++++- .../redundant_pattern_matching_option.fixed | 8 + tests/ui/redundant_pattern_matching_option.rs | 8 + .../redundant_pattern_matching_option.stderr | 8 +- tests/ui/slow_vector_initialization.rs | 26 ++-- tests/ui/slow_vector_initialization.stderr | 143 ++++++++++-------- tests/ui/starts_ends_with.fixed | 2 +- tests/ui/starts_ends_with.rs | 2 +- tests/ui/trailing_empty_array.rs | 14 ++ tests/ui/unnecessary_map_or.fixed | 14 +- tests/ui/unnecessary_map_or.rs | 4 + tests/ui/unnecessary_map_or.stderr | 66 +++++--- tests/ui/useless_vec.rs | 15 ++ tests/ui/useless_vec.stderr | 21 +++ 146 files changed, 2002 insertions(+), 434 deletions(-) create mode 100644 clippy_lints/src/methods/double_ended_iterator_last.rs create mode 100644 tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml create mode 100644 tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed create mode 100644 tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs create mode 100644 tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr create mode 100644 tests/ui/crashes/ice-10972-tait.rs create mode 100644 tests/ui/crashes/ice-13862.rs create mode 100644 tests/ui/double_ended_iterator_last.fixed create mode 100644 tests/ui/double_ended_iterator_last.rs create mode 100644 tests/ui/double_ended_iterator_last.stderr create mode 100644 tests/ui/useless_vec.rs create mode 100644 tests/ui/useless_vec.stderr diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index bcb3193ad670..d6534fbaff94 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -17,6 +17,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Build diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index 496220480508..dee7d028655e 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -23,6 +23,8 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.ref }} + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Check Changelog @@ -63,6 +65,8 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install i686 dependencies if: matrix.host == 'i686-unknown-linux-gnu' @@ -74,7 +78,8 @@ jobs: - name: Install toolchain run: | rustup set default-host ${{ matrix.host }} - rustup show active-toolchain + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build @@ -121,9 +126,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install - name: Test metadata collection run: cargo collect-metadata @@ -136,9 +145,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build Integration Test @@ -188,9 +201,13 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Download - name: Download target dir @@ -205,7 +222,7 @@ jobs: # Run - name: Test ${{ matrix.integration }} run: | - TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ') + TOOLCHAIN=$(rustup show active-toolchain | head -n 1 | cut -f1 -d' ') rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output env: INTEGRATION: ${{ matrix.integration }} diff --git a/.github/workflows/clippy_pr.yml b/.github/workflows/clippy_pr.yml index 2e5b5bd41dfb..80523d91f4fc 100644 --- a/.github/workflows/clippy_pr.yml +++ b/.github/workflows/clippy_pr.yml @@ -25,9 +25,14 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Install toolchain - run: rustup show active-toolchain + run: | + # Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0 + rustup show active-toolchain || rustup toolchain install # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 32dc251c836f..b42f3e7712f1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,19 +22,27 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Checkout uses: actions/checkout@v4 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # Run - name: Set tag name if: startswith(github.ref, 'refs/tags/') run: | - TAG=$(basename ${{ github.ref }}) + TAG=$(basename "${TAGNAME}") echo "TAG_NAME=$TAG" >> $GITHUB_ENV + env: + # Make sure that the reference gets expanded before injecting it + TAGNAME: ${{ github.ref }} - name: Set beta to true if: github.ref == 'refs/heads/beta' run: echo "BETA=true" >> $GITHUB_ENV diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 3cbda0b38243..64966f1d1898 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -21,6 +21,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 2 + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false # HEAD is the generated merge commit `refs/pull/N/merge` between the PR and `master`, `HEAD^` # being the commit from `master` that is the base of the merge @@ -73,6 +75,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Cache lintcheck bin id: cache-lintcheck-bin @@ -103,6 +108,9 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Restore lintcheck bin uses: actions/cache/restore@v4 diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 0d402fe70641..69d00dc027e8 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -12,6 +12,9 @@ jobs: # Setup - name: Checkout uses: actions/checkout@v4 + with: + # Unsetting this would make so that any malicious package could get our Github Token + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index b6033de93501..1770e8095a01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,52 @@ document. ## Unreleased / Beta / In Rust Nightly -[aa0d5513...master](https://github.com/rust-lang/rust-clippy/compare/aa0d5513...master) +[786fbd6d...master](https://github.com/rust-lang/rust-clippy/compare/786fbd6d...master) + +## Rust 1.84 + +Current stable, released 2025-01-09 + +[View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster) + +### New Lints + +* Added [`unnecessary_map_or`] to `style` + [#11796](https://github.com/rust-lang/rust-clippy/pull/11796) +* Added [`arbitrary_source_item_ordering`] to `restriction` + [#13376](https://github.com/rust-lang/rust-clippy/pull/13376) +* Added [`map_with_unused_argument_over_ranges`] to `restriction` + [#13034](https://github.com/rust-lang/rust-clippy/pull/13034) +* Added [`map_all_any_identity`] to `complexity` + [#13499](https://github.com/rust-lang/rust-clippy/pull/13499) +* Added [`needless_as_bytes`] to `complexity` + [#13437](https://github.com/rust-lang/rust-clippy/pull/13437) +* Added [`unnecessary_literal_bound`] to `pedantic` + [#13395](https://github.com/rust-lang/rust-clippy/pull/13395) +* Added [`manual_ignore_case_cmp`] to `perf` + [#13334](https://github.com/rust-lang/rust-clippy/pull/13334) +* Added [`regex_creation_in_loops`] to `perf` + [#13412](https://github.com/rust-lang/rust-clippy/pull/13412) + +### Moves and Deprecations + +* Moved [`manual_is_power_of_two`] to `pedantic` (From `complexity`, now allow-by-default) + [#13553](https://github.com/rust-lang/rust-clippy/pull/13553) +* Move [`module_name_repetitions`] to `restriction` (from `pedantic`) + [#13541](https://github.com/rust-lang/rust-clippy/pull/13541) + +### Enhancements + +* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: + CoAP, MHz, GHz, and THz + [#13633](https://github.com/rust-lang/rust-clippy/pull/13633) + [#13460](https://github.com/rust-lang/rust-clippy/pull/13460) +* [`large_const_arrays`]: Changed the default of [`array-size-threshold`] to `16kb` (from `512kb`) + [#13485](https://github.com/rust-lang/rust-clippy/pull/13485) ## Rust 1.83 -Current stable, released 2024-11-28 +Released 2024-11-28 [View all 64 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-08-25T09%3A59%3A01Z..2024-10-03T13%3A42%3A56Z+base%3Amaster) @@ -5493,6 +5534,7 @@ Released 2018-09-13 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons +[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens @@ -6252,6 +6294,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold +[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/Cargo.toml b/Cargo.toml index 77cb0006ff85..efa59fecc0e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index df9b1bbe18f3..2b2c096b0496 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -83,7 +83,12 @@ As section headers, we use: ``` ### New Lints +* Added [`LINT`] to `GROUP` + ### Moves and Deprecations +* Moved [`LINT`] to `GROUP` (From `GROUP`, now LEVEL-by-default) +* Renamed `LINT` to [`LINT`] + ### Enhancements ### False Positive Fixes ### Suggestion Fixes/Improvements diff --git a/book/src/development/method_checking.md b/book/src/development/method_checking.md index 9c5d4b516db2..b3126024b990 100644 --- a/book/src/development/method_checking.md +++ b/book/src/development/method_checking.md @@ -21,7 +21,7 @@ use clippy_utils::is_trait_method; impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { // Check our expr is calling a method with pattern matching - if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind // Check if the name of this method is `our_fancy_method` && path.ident.name.as_str() == "our_fancy_method" // We can check the type of the self argument whenever necessary. diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index ea1d7d11389d..181e794e6e46 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -582,6 +582,33 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +## `lint-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `literal-representation-threshold` The lower bound for linting decimal literals diff --git a/clippy.toml b/clippy.toml index a7b0cc56ea12..f4789c9d0303 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,5 +1,7 @@ avoid-breaking-exported-api = false +lint-inconsistent-struct-field-initializers = true + [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 3f18a0bc7d25..c761e207c6b6 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_config" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" publish = false diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index bffa04f6f090..c616589c56e0 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -532,6 +532,26 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] literal_representation_threshold: u64 = 16384, diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index c66738592820..790dafa811f9 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -179,8 +179,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -191,8 +191,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -220,8 +220,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { } fields.push(ClippyConf { name, - lints, attrs: &conf[attrs_start..attrs_end], + lints, field: conf[field_start..].trim_end(), }); diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index c1f8e82f6988..b575ac1bf4cc 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index 741539902662..380b094d017e 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -428,8 +428,8 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // Makes a note of the current item for comparison with the next. cur_t = Some(CurItem { - order: module_level_order, item, + order: module_level_order, name: get_item_name(item), }); } @@ -464,7 +464,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte ItemKind::Use(..) => Use, ItemKind::Static(..) => Static, ItemKind::Const(..) => Const, - ItemKind::Fn{ .. } => Fn, + ItemKind::Fn { .. } => Fn, ItemKind::Macro(..) => Macro, ItemKind::Mod(..) => Mod, ItemKind::ForeignMod { .. } => ForeignMod, diff --git a/clippy_lints/src/attrs/mixed_attributes_style.rs b/clippy_lints/src/attrs/mixed_attributes_style.rs index 32c28c09c360..8c91c65eaf76 100644 --- a/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -66,7 +66,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion()); - let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) { + let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.next_back()) { first.span.with_hi(last.span.hi()) } else { return; diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index 67aa33ca06c3..6057144bc6a4 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::{is_lint_allowed, msrvs, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; +use rustc_span::BytePos; use super::BORROW_AS_PTR; @@ -32,12 +34,21 @@ pub(super) fn check<'tcx>( return false; } - let suggestion = if msrv.meets(msrvs::RAW_REF_OP) { + let (suggestion, span) = if msrv.meets(msrvs::RAW_REF_OP) { let operator_kind = match mutability { Mutability::Not => "const", Mutability::Mut => "mut", }; - format!("&raw {operator_kind} {snip}") + // Make sure that the span to be replaced doesn't include parentheses, that could break the + // suggestion. + let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) { + expr.span + .with_lo(expr.span.lo() + BytePos(1)) + .with_hi(expr.span.hi() - BytePos(1)) + } else { + expr.span + }; + (format!("&raw {operator_kind} {snip}"), span) } else { let Some(std_or_core) = std_or_core(cx) else { return false; @@ -46,18 +57,10 @@ pub(super) fn check<'tcx>( Mutability::Not => "addr_of", Mutability::Mut => "addr_of_mut", }; - format!("{std_or_core}::ptr::{macro_name}!({snip})") + (format!("{std_or_core}::ptr::{macro_name}!({snip})"), expr.span) }; - span_lint_and_sugg( - cx, - BORROW_AS_PTR, - expr.span, - "borrow as raw pointer", - "try", - suggestion, - Applicability::MachineApplicable, - ); + span_lint_and_sugg(cx, BORROW_AS_PTR, span, "borrow as raw pointer", "try", suggestion, app); return true; } false diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index c64c0e15144d..d90cf124fe42 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -836,11 +836,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { as_underscore::check(cx, expr, cast_to_hir); as_pointer_underscore::check(cx, cast_to, cast_to_hir); - let was_borrow_as_ptr_emitted = if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv) - } else { - false - }; + let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR) + && borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv); if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted { ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7451fb909ef8..3ff10d850f82 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -372,6 +372,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, + crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 40377dd841e0..3e2b7055de4d 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,6 +1,6 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::{is_doc_hidden, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; @@ -70,7 +70,14 @@ pub fn check( && let typeck = cx.tcx.typeck_body(body_id) && let body = cx.tcx.hir().body(body_id) && let ret_ty = typeck.expr_ty(body.value) - && implements_trait(cx, ret_ty, future, &[]) + && implements_trait_with_env( + cx.tcx, + ty::TypingEnv::non_body_analysis(cx.tcx, owner_id.def_id), + ret_ty, + future, + Some(owner_id.def_id.to_def_id()), + &[], + ) && let ty::Coroutine(_, subs) = ret_ty.kind() && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result) { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index c835b81679b5..7561a6cf2a78 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -797,8 +797,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ parser.into_offset_iter(), &doc, Fragments { - fragments: &fragments, doc: &doc, + fragments: &fragments, }, )) } diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 2327da0ccff7..1f89cab91480 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -25,7 +25,7 @@ pub(super) fn check( // page. So associated items or impl blocks are not part of this list. ItemKind::Static(..) | ItemKind::Const(..) - | ItemKind::Fn{ .. } + | ItemKind::Fn { .. } | ItemKind::Macro(..) | ItemKind::Mod(..) | ItemKind::TyAlias(..) diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 70524e458c78..2bec4f2f99e5 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -678,12 +678,12 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: Vec::new(), - is_map_used: false, allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, + is_map_used: false, + edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), }; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2cd48ef98e52..c0b4743fd71c 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -182,7 +182,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when // T is a generic type. For example, return type of `Option::as_deref()` is a generic. // So we have a hack like this. - && generic_args.len() > 0 + && !generic_args.is_empty() { return; } diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index 4427edb752e0..ef272c305d34 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -243,11 +243,11 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds && !predicates.is_empty() { Some(ImplTraitBound { + span: bound.span(), predicates, + trait_def_id, args: path.args.map_or([].as_slice(), |p| p.args), constraints: path.args.map_or([].as_slice(), |p| p.constraints), - trait_def_id, - span: bound.span(), }) } else { None diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 4fcd2abb7694..39ff3c13bcce 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,19 +1,21 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fulfill_or_allowed; use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind, StructTailExpr}; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::fmt::{self, Write as _}; declare_clippy_lint! { /// ### What it does - /// Checks for struct constructors where all fields are shorthand and - /// the order of the field init shorthand in the constructor is inconsistent - /// with the order in the struct definition. + /// Checks for struct constructors where the order of the field + /// init in the constructor is inconsistent with the order in the + /// struct definition. /// /// ### Why is this bad? /// Since the order of fields in a constructor doesn't affect the @@ -59,16 +61,37 @@ declare_clippy_lint! { #[clippy::version = "1.52.0"] pub INCONSISTENT_STRUCT_CONSTRUCTOR, pedantic, - "the order of the field init shorthand is inconsistent with the order in the struct definition" + "the order of the field init is inconsistent with the order in the struct definition" } -declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); +pub struct InconsistentStructConstructor { + lint_inconsistent_struct_field_initializers: bool, +} + +impl InconsistentStructConstructor { + pub fn new(conf: &'static Conf) -> Self { + Self { + lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + } + } +} + +impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let ExprKind::Struct(qpath, fields, base) = expr.kind - && fields.iter().all(|f| f.is_shorthand) - && !expr.span.from_expansion() + let ExprKind::Struct(_, fields, _) = expr.kind else { + return; + }; + let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); + let applicability = if all_fields_are_shorthand { + Applicability::MachineApplicable + } else if self.lint_inconsistent_struct_field_initializers { + Applicability::MaybeIncorrect + } else { + return; + }; + if !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() @@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { return; } - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); - - let base_snippet = if let StructTailExpr::Base(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; - - let sugg = format!( - "{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); + let span = field_with_attrs_span(cx.tcx, fields.first().unwrap()) + .with_hi(field_with_attrs_span(cx.tcx, fields.last().unwrap()).hi()); if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) { - span_lint_and_sugg( + span_lint_and_then( cx, INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, + span, "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + let msg = if all_fields_are_shorthand { + "try" + } else { + "if the field evaluation order doesn't matter, try" + }; + let sugg = suggestion(cx, fields, &def_order_map); + diag.span_suggestion(span, msg, sugg, applicability); + }, ); } } @@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map true } + +fn suggestion<'tcx>( + cx: &LateContext<'_>, + fields: &'tcx [hir::ExprField<'tcx>], + def_order_map: &FxHashMap, +) -> String { + let ws = fields + .windows(2) + .map(|w| { + let w0_span = field_with_attrs_span(cx.tcx, &w[0]); + let w1_span = field_with_attrs_span(cx.tcx, &w[1]); + let span = w0_span.between(w1_span); + snippet(cx, span, " ") + }) + .collect::>(); + + let mut fields = fields.to_vec(); + fields.sort_unstable_by_key(|field| def_order_map[&field.ident.name]); + let field_snippets = fields + .iter() + .map(|field| snippet(cx, field_with_attrs_span(cx.tcx, field), "..")) + .collect::>(); + + assert_eq!(field_snippets.len(), ws.len() + 1); + + let mut sugg = String::new(); + for i in 0..field_snippets.len() { + sugg += &field_snippets[i]; + if i < ws.len() { + sugg += &ws[i]; + } + } + sugg +} + +fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { + if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { + field.span.with_lo(attr.span.lo()) + } else { + field.span + } +} diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 1c63ca9d9743..5418acc105eb 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::ty::implements_trait; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -626,18 +627,30 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); - match ty.kind() { - ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { - let is_empty = sym!(is_empty); - cx.tcx - .associated_items(principal.def_id()) - .filter_by_name_unhygienic(is_empty) - .any(|item| is_is_empty(cx, item)) - }), - ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), - ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), - ty::Array(..) | ty::Slice(..) | ty::Str => true, - _ => false, + fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool { + match ty.kind() { + ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| { + let is_empty = sym!(is_empty); + cx.tcx + .associated_items(principal.def_id()) + .filter_by_name_unhygienic(is_empty) + .any(|item| is_is_empty(cx, item)) + }), + ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id), + ty::Adt(id, _) => { + has_is_empty_impl(cx, id.did()) + || (cx.tcx.recursion_limit().value_within_limit(depth) + && cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| { + implements_trait(cx, ty, deref_id, &[]) + && cx + .get_associated_type(ty, deref_id, "Target") + .is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1)) + })) + }, + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } } + + ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d33013ba6638..fad6f9d08803 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -649,7 +649,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); - store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); + store.register_late_pass(move |_| { + Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( + conf, + )) + }); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index f88605fbea0d..6be30f3c957b 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -38,8 +38,8 @@ pub(super) fn check<'tcx>( cx, label, inner_labels: label.into_iter().collect(), - is_finite: false, loop_depth: 0, + is_finite: false, }; loop_visitor.visit_block(loop_block); diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 312bcb55a953..006addb987f5 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -220,11 +220,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span let mut vis = BodyVisitor { + macro_unsafe_blocks: Vec::new(), #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, - macro_unsafe_blocks: Vec::new(), - lint: self, - cx + cx, + lint: self }; vis.visit_body(body); } diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index bbb89bee8355..aa59b047b169 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -2,14 +2,15 @@ use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::sugg::Sugg; -use rustc_ast::{BinOpKind, LitKind}; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self}; use rustc_session::impl_lint_pass; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use clippy_config::Conf; @@ -138,9 +139,40 @@ fn build_suggestion( applicability: &mut Applicability, ) { let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric() + && matches!( + lhs.kind, + ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }) | ExprKind::Unary(UnOp::Neg, Expr { + kind: ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }), + .. + }) + ) { + format!("_{}", cx.typeck_results().expr_ty(rhs)) + } else { + String::new() + }; + let dividend_sugg_str = dividend_sugg.into_string(); + // If `dividend_sugg` has enclosing paren like `(-2048)` and we need to add type suffix in the + // suggestion message, we want to make a suggestion string before `div_ceil` like + // `(-2048_{type_suffix})`. + let suggestion_before_div_ceil = if has_enclosing_paren(÷nd_sugg_str) { + format!( + "{}{})", + ÷nd_sugg_str[..dividend_sugg_str.len() - 1].to_string(), + type_suffix + ) + } else { + format!("{dividend_sugg_str}{type_suffix}") + }; let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); - let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})"); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/manual_ignore_case_cmp.rs b/clippy_lints/src/manual_ignore_case_cmp.rs index dabfac3f6137..506f4f6d9de1 100644 --- a/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/clippy_lints/src/manual_ignore_case_cmp.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") /// } /// ``` - #[clippy::version = "1.82.0"] + #[clippy::version = "1.84.0"] pub MANUAL_IGNORE_CASE_CMP, perf, "manual case-insensitive ASCII comparison" diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 5b9eedd9b8bf..9860deba8432 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg, start); + let ty_sugg = get_ty_sugg(cx, arg); let range = check_expr_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } @@ -123,19 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { extract_msrv_attr!(LateContext); } -fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> { - if let ExprKind::Lit(lit) = bound_expr.kind - && let local_hid = path_to_local(arg)? - && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) +fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { + let local_hid = path_to_local(arg)?; + if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span { - let ty_str = match lit.node { - Char(_) => "char", - Byte(_) => "u8", - _ => return None, - }; - return Some((*ty_span, ty_str)); + let arg_type = cx.typeck_results().expr_ty(arg); + return Some((*ty_span, arg_type)); } None } @@ -145,7 +140,7 @@ fn check_is_ascii( span: Span, recv: &Expr<'_>, range: &CharRange, - ty_sugg: Option<(Span, &'_ str)>, + ty_sugg: Option<(Span, Ty<'_>)>, ) { let sugg = match range { CharRange::UpperChar => "is_ascii_uppercase", @@ -159,8 +154,8 @@ fn check_is_ascii( let mut app = Applicability::MachineApplicable; let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; - if let Some((ty_span, ty_str)) = ty_sugg { - suggestion.push((ty_span, format!("{recv}: {ty_str}"))); + if let Some((ty_span, ty)) = ty_sugg { + suggestion.push((ty_span, format!("{recv}: {ty}"))); } span_lint_and_then( @@ -206,7 +201,6 @@ fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { } } - fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 1fd2ebcb54a6..ac1eae07eff6 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -583,11 +583,6 @@ declare_clippy_lint! { /// are the same on purpose, you can factor them /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). /// - /// ### Known problems - /// False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// /// ### Example /// ```rust,ignore /// match foo { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 7e74b36b4415..edac97344a03 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,6 +1,6 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, walk_span_to_context}; +use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; @@ -274,7 +274,9 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; - let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_")); + let mut app = Applicability::MachineApplicable; + let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par(); + let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying! @@ -307,7 +309,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op format!("redundant pattern matching, consider using `{good_method}`"), "try", sugg, - Applicability::MachineApplicable, + app, ); } } diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index b1d0686ffc42..38f876fed802 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -345,7 +345,7 @@ impl<'a> PatState<'a> { PatKind::Guard(..) => { matches!(self, Self::Wild) - } + }, // Patterns for things which can only contain a single sub-pattern. PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { diff --git a/clippy_lints/src/methods/double_ended_iterator_last.rs b/clippy_lints/src/methods/double_ended_iterator_last.rs new file mode 100644 index 000000000000..208172980c9f --- /dev/null +++ b/clippy_lints/src/methods/double_ended_iterator_last.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Instance; +use rustc_span::{Span, sym}; + +use super::DOUBLE_ENDED_ITERATOR_LAST; + +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Expr<'_>, call_span: Span) { + let typeck = cx.typeck_results(); + + // if the "last" method is that of Iterator + if is_trait_method(cx, expr, sym::Iterator) + // if self implements DoubleEndedIterator + && let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator) + && let self_type = cx.typeck_results().expr_ty(self_expr) + && implements_trait(cx, self_type.peel_refs(), deiter_id, &[]) + // resolve the method definition + && let id = typeck.type_dependent_def_id(expr.hir_id).unwrap() + && let args = typeck.node_args(expr.hir_id) + && let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args) + // find the provided definition of Iterator::last + && let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last") + // if the resolved method is the same as the provided definition + && fn_def.def_id() == last_def.def_id + { + span_lint_and_sugg( + cx, + DOUBLE_ENDED_ITERATOR_LAST, + call_span, + "called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator", + "try", + "next_back()".to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 07a7a12b1627..f7bb8c1d696d 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -17,10 +17,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ let mut applicability = Applicability::MachineApplicable; let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); + let span = expr.span.with_lo(map_span.lo()); + // If the methods are separated with comments, we don't apply suggestion automatically. + if span_contains_comment(cx.tcx.sess.source_map(), span) { + applicability = Applicability::Unspecified; + } span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span.with_lo(map_span.lo()), + span, format!("called `map(..).flatten()` on `{caller_ty_name}`"), format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), format!("{method_to_use}({closure_snippet})"), diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 1f204de01da0..053601446573 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; +use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; +use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node, PatKind}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; @@ -24,6 +25,16 @@ pub(super) fn check( && is_expr_untyped_identity_function(cx, map_arg) && let Some(sugg_span) = expr.span.trim_start(caller.span) { + // If the result of `.map(identity)` is used as a mutable reference, + // the caller must not be an immutable binding. + if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() + && let Some(hir_id) = path_to_local(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) + { + return; + } + span_lint_and_sugg( cx, MAP_IDENTITY, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 810287fa5416..51351f6b7cd1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -14,6 +14,7 @@ mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; mod collapsible_str_replace; +mod double_ended_iterator_last; mod drain_collect; mod err_expect; mod expect_fun_call; @@ -4284,6 +4285,32 @@ declare_clippy_lint! { "map of a trivial closure (not dependent on parameter) over a range" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced + /// with `DoubleEndedIterator::next_back`. + /// + /// ### Why is this bad? + /// + /// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if + /// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization, + /// `Iterator::last` cannot be optimized for `DoubleEndedIterator`. + /// + /// ### Example + /// ```no_run + /// let last_arg = "echo hello world".split(' ').last(); + /// ``` + /// Use instead: + /// ```no_run + /// let last_arg = "echo hello world".split(' ').next_back(); + /// ``` + #[clippy::version = "1.85.0"] + pub DOUBLE_ENDED_ITERATOR_LAST, + perf, + "using `Iterator::last` on a `DoubleEndedIterator`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4449,6 +4476,7 @@ impl_lint_pass!(Methods => [ MAP_ALL_ANY_IDENTITY, MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, + DOUBLE_ENDED_ITERATOR_LAST, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4931,6 +4959,7 @@ impl Methods { false, ); } + double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs index 348f740e7ddf..6993150fb57a 100644 --- a/clippy_lints/src/methods/needless_character_iteration.rs +++ b/clippy_lints/src/methods/needless_character_iteration.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::CHAR_IS_ASCII; use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; @@ -77,7 +78,7 @@ fn handle_expr( if revert != is_all && let ExprKind::Path(path) = fn_path.kind && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() - && match_def_path(cx, fn_def_id, &["core", "char", "methods", "", "is_ascii"]) + && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII) && path_to_local_id(peels_expr_ref(arg), first_param) && let Some(snippet) = before_chars.get_source_text(cx) { diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index ea4984f83adb..2780c3f8af5c 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( captured_ids: HirIdSet, ) -> Option> { let mut visitor = IterFunctionVisitor { - uses: Vec::new(), - target: id, - seen_other: false, - cx, - current_mutably_captured_ids: HirIdSet::default(), illegal_mutable_capture_ids: captured_ids, + current_mutably_captured_ids: HirIdSet::default(), + cx, + uses: Vec::new(), hir_id_uses_map: FxHashMap::default(), current_statement_hir_id: None, + seen_other: false, + target: id, }; visitor.visit_block(block); if visitor.seen_other { diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index db2b9d4d92fb..82e66a0500a8 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::STDIN; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_local_use_after_expr; @@ -33,7 +34,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"]) + && match_def_path(cx, recv_adt.did(), &STDIN) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index c6d4ef5911ee..8a99974394c3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>( { Some(IterUsage { kind: IterUsageKind::NextTuple, - span: e.span, unwrap_kind: None, + span: e.span, }) } else { None diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index b5d8972d7aad..c27d1fb4903b 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -124,30 +124,30 @@ pub(super) fn check( match lit.node { ast::LitKind::Bool(false) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + method_name: "any", has_args: true, has_generic_return: false, - method_name: "any", }); }, ast::LitKind::Bool(true) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + method_name: "all", has_args: true, has_generic_return: false, - method_name: "all", }); }, ast::LitKind::Int(Pu128(0), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { + method_name: "sum", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "sum", }); }, ast::LitKind::Int(Pu128(1), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + method_name: "product", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "product", }); }, _ => (), diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 1199d2897610..b7dbebe60a42 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -7,7 +7,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_from_proc_macro, path_to_local_id}; +use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id}; use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; @@ -96,11 +96,25 @@ pub(super) fn check<'a>( Sugg::hir(cx, non_binding_location, "") ))); - let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding) - .maybe_par() - .into_string(); + let mut app = Applicability::MachineApplicable; + let binop = make_binop( + op.node, + &Sugg::hir_with_applicability(cx, recv, "..", &mut app), + &inner_non_binding, + ); - (binop, "a standard comparison", Applicability::MaybeIncorrect) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { + match parent_expr.kind { + ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(), + ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(), + _ => binop, + } + } else { + binop + } + .into_string(); + + (sugg, "a standard comparison", app) } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 42107581ab46..964f1603f0e5 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -494,7 +494,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { match node { Node::Stmt(_) => return true, - Node::Block(..) => continue, + Node::Block(..) => {}, Node::Item(item) => { if let ItemKind::Fn { body: body_id, .. } = &item.kind && let output_ty = return_ty(cx, item.owner_id) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 121c4326d648..2572e186ce6c 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; -use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, is_in_test, trait_ref_of_method}; use rustc_errors::Applicability; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -97,6 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, def_id: LocalDefId, ) { + let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); + if is_in_test(cx.tcx, hir_id) { + return; + } + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -136,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { return; } - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - // Const fns are not allowed as methods in a trait. { let parent = cx.tcx.hir().get_parent_item(hir_id).def_id; diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 1141728640d4..29dcbaa9e62a 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { // ignore main() if it.ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index b18f18d89e56..05aa425de9ed 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { return; } match it.kind { - hir::ItemKind::Fn{ .. } => { + hir::ItemKind::Fn { .. } => { let desc = "a function"; let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 3c47d0edfdc5..5f7fde30f03f 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -80,7 +81,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ mut self".to_string() } else { - format!("&{} mut self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} mut self") } }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), @@ -89,7 +91,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod applicability = Applicability::HasPlaceholders; "&'_ self".to_string() } else { - format!("&{} self", &lifetime.ident.name) + let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); + format!("&{lt_name} self") } }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index c48232f99058..05b31fc84b9b 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{indent_of, snippet, snippet_block}; -use rustc_ast::ast; +use rustc_ast::{Block, Label, ast}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -11,6 +11,7 @@ declare_clippy_lint! { /// that contain a `continue` statement in either their main blocks or their /// `else`-blocks, when omitting the `else`-block possibly with some /// rearrangement of code can make the code easier to understand. + /// The lint also checks if the last statement in the loop is a `continue` /// /// ### Why is this bad? /// Having explicit `else` blocks for `if` statements @@ -75,6 +76,49 @@ declare_clippy_lint! { /// # break; /// } /// ``` + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// continue + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// continue + /// } + /// _ => { + /// eprintln!("other error"); + /// continue + /// } + /// } + /// } + /// ``` + /// Could be rewritten as + /// + /// + /// ```rust + /// # use std::io::ErrorKind; + /// + /// fn foo() -> ErrorKind { ErrorKind::NotFound } + /// for _ in 0..10 { + /// match foo() { + /// ErrorKind::NotFound => { + /// eprintln!("not found"); + /// } + /// ErrorKind::TimedOut => { + /// eprintln!("timeout"); + /// } + /// _ => { + /// eprintln!("other error"); + /// } + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_CONTINUE, pedantic, @@ -144,7 +188,7 @@ impl EarlyLintPass for NeedlessContinue { /// /// - The expression is a `continue` node. /// - The expression node is a block with the first statement being a `continue`. -fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool { +fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool { match else_expr.kind { ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label), ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()), @@ -152,7 +196,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) } } -fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool { +fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool { block.stmts.first().is_some_and(|stmt| match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { if let ast::ExprKind::Continue(ref l) = e.kind { @@ -166,7 +210,7 @@ fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) } /// If the `continue` has a label, check it matches the label of the loop. -fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool { +fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> bool { match (loop_label, continue_label) { // `loop { continue; }` or `'a loop { continue; }` (_, None) => true, @@ -181,7 +225,7 @@ fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast:: /// the AST object representing the loop block of `expr`. fn with_loop_block(expr: &ast::Expr, mut func: F) where - F: FnMut(&ast::Block, Option<&ast::Label>), + F: FnMut(&Block, Option<&Label>), { if let ast::ExprKind::While(_, loop_block, label) | ast::ExprKind::ForLoop { @@ -205,7 +249,7 @@ where /// - The `else` expression. fn with_if_expr(stmt: &ast::Stmt, mut func: F) where - F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr), + F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr), { match stmt.kind { ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => { @@ -231,14 +275,14 @@ struct LintData<'a> { /// The condition expression for the above `if`. if_cond: &'a ast::Expr, /// The `then` block of the `if` statement. - if_block: &'a ast::Block, + if_block: &'a Block, /// The `else` block of the `if` statement. /// Note that we only work with `if` exprs that have an `else` branch. else_expr: &'a ast::Expr, /// The 0-based index of the `if` statement in the containing loop block. stmt_idx: usize, /// The statements of the loop block. - loop_block: &'a ast::Block, + loop_block: &'a Block, } const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant"; @@ -329,33 +373,74 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin ) } -fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind - && let Some(last_stmt) = loop_block.stmts.last() - && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind - && let ast::ExprKind::Continue(continue_label) = inner_expr.kind - && compare_labels(loop_label.as_ref(), continue_label.as_ref()) - { - span_lint_and_help( - cx, - NEEDLESS_CONTINUE, - last_stmt.span, - MSG_REDUNDANT_CONTINUE_EXPRESSION, - None, - DROP_CONTINUE_EXPRESSION_MSG, - ); +fn check_last_stmt_in_expr(inner_expr: &ast::Expr, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + match &inner_expr.kind { + ast::ExprKind::Continue(continue_label) => { + func(continue_label.as_ref(), inner_expr.span); + }, + ast::ExprKind::If(_, then_block, else_block) => { + check_last_stmt_in_block(then_block, func); + if let Some(else_block) = else_block { + check_last_stmt_in_expr(else_block, func); + } + }, + ast::ExprKind::Match(_, arms, _) => { + for arm in arms { + if let Some(expr) = &arm.body { + check_last_stmt_in_expr(expr, func); + } + } + }, + ast::ExprKind::Block(b, _) => { + check_last_stmt_in_block(b, func); + }, + _ => {}, } +} + +fn check_last_stmt_in_block(b: &Block, func: &F) +where + F: Fn(Option<&Label>, Span), +{ + if let Some(last_stmt) = b.stmts.last() + && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind + { + check_last_stmt_in_expr(inner_expr, func); + } +} + +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { with_loop_block(expr, |loop_block, label| { - for (i, stmt) in loop_block.stmts.iter().enumerate() { + let p = |continue_label: Option<&Label>, span: Span| { + if compare_labels(label, continue_label) { + span_lint_and_help( + cx, + NEEDLESS_CONTINUE, + span, + MSG_REDUNDANT_CONTINUE_EXPRESSION, + None, + DROP_CONTINUE_EXPRESSION_MSG, + ); + } + }; + + let stmts = &loop_block.stmts; + for (i, stmt) in stmts.iter().enumerate() { + let mut maybe_emitted_in_if = false; with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { let data = &LintData { - stmt_idx: i, if_expr, if_cond: cond, if_block: then_block, else_expr, + stmt_idx: i, loop_block, }; + + maybe_emitted_in_if = true; if needless_continue_in_else(else_expr, label) { emit_warning( cx, @@ -365,8 +450,14 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { ); } else if is_first_block_stmt_continue(then_block, label) { emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock); + } else { + maybe_emitted_in_if = false; } }); + + if i == stmts.len() - 1 && !maybe_emitted_in_if { + check_last_stmt_in_block(loop_block, &p); + } } }); } @@ -400,7 +491,7 @@ fn erode_from_back(s: &str) -> String { if ret.is_empty() { s.to_string() } else { ret } } -fn span_of_first_expr_in_block(block: &ast::Block) -> Option { +fn span_of_first_expr_in_block(block: &Block) -> Option { block.stmts.first().map(|stmt| stmt.span) } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 92644456f63d..ccd507580445 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -144,7 +144,7 @@ impl NoEffect { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn{ .. } = item.kind + && let ItemKind::Fn { .. } = item.kind && let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index ebd301d5156a..8409d179b0f5 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -189,6 +189,8 @@ impl<'tcx> NonCopyConst<'tcx> { } fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool { + // No branch that we check (yet) should continue if val isn't a ValTree::Branch + let ty::ValTree::Branch(val) = val else { return false }; match *ty.kind() { // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. @@ -197,12 +199,13 @@ impl<'tcx> NonCopyConst<'tcx> { // contained value. ty::Adt(def, ..) if def.is_union() => false, ty::Array(ty, _) => val - .unwrap_branch() .iter() .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Adt(def, args) if def.is_enum() => { - let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); - let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32()); + let Some((&ty::ValTree::Leaf(variant_index), fields)) = val.split_first() else { + return false; + }; + let variant_index = VariantIdx::from_u32(variant_index.to_u32()); fields .iter() .copied() @@ -215,12 +218,10 @@ impl<'tcx> NonCopyConst<'tcx> { .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty)) }, ty::Adt(def, args) => val - .unwrap_branch() .iter() .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), ty::Tuple(tys) => val - .unwrap_branch() .iter() .zip(tys) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)), diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 2fee1c72a91b..56c4157d6fe0 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -270,10 +270,10 @@ impl SimilarNamesNameVisitor<'_, '_, '_> { return; } self.0.names.push(ExistingName { - exemptions: get_exemptions(interned_name).unwrap_or(&[]), interned: ident.name, span: ident.span, len: count, + exemptions: get_exemptions(interned_name).unwrap_or(&[]), }); } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 65ef56fd2112..0eca788c7874 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -47,6 +47,7 @@ impl ArithmeticSideEffects { Self { allowed_binary, allowed_unary, + const_span: None, disallowed_int_methods: [ sym::saturating_div, sym::wrapping_div, @@ -55,7 +56,6 @@ impl ArithmeticSideEffects { ] .into_iter() .collect(), - const_span: None, expr_span: None, } } diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index d2529d4d9f85..668f09bbfd58 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -143,11 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { self.searcher = Some(PathbufPushSearcher { local_id: id, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), - err_span: local.span, init_val: *init_expr, arg: None, + name: name.name, + err_span: local.span, }); } } @@ -165,10 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { local_id: id, lhs_is_let: false, let_ty_span: None, - name: name.ident.name, - err_span: expr.span, init_val: *right, arg: None, + name: name.ident.name, + err_span: expr.span, }); } } diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs index 6a1d40334e75..a27f9b631143 100644 --- a/clippy_lints/src/redundant_else.rs +++ b/clippy_lints/src/redundant_else.rs @@ -69,7 +69,6 @@ impl EarlyLintPass for RedundantElse { ExprKind::If(_, next_then, Some(next_els)) => { then = next_then; els = next_els; - continue; }, // else if without else ExprKind::If(..) => return, diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index 4f46ca3c7150..658d93e634cf 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -17,9 +17,9 @@ declare_clippy_lint! { /// Checks for redundant redefinitions of local bindings. /// /// ### Why is this bad? - /// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended. + /// Redundant redefinitions of local bindings do not change behavior other than variable's lifetimes and are likely to be unintended. /// - /// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation. + /// These rebindings can be intentional to shorten the lifetimes of variables because they affect when the `Drop` implementation is called. Other than that, they do not affect your code's meaning but they _may_ affect `rustc`'s stack allocation. /// /// ### Example /// ```no_run @@ -41,7 +41,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub REDUNDANT_LOCALS, - correctness, + suspicious, "redundant redefinition of a local binding" } declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 6a5bf1b8045d..9443dca154e3 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -24,6 +24,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("(") /// ``` + /// + /// Use instead: + /// ```ignore + /// Regex::new("\(") + /// ``` #[clippy::version = "pre 1.29.0"] pub INVALID_REGEX, correctness, @@ -49,6 +54,11 @@ declare_clippy_lint! { /// ```ignore /// Regex::new("^foobar") /// ``` + /// + /// Use instead: + /// ```ignore + /// str::starts_with("foobar") + /// ``` #[clippy::version = "pre 1.29.0"] pub TRIVIAL_REGEX, nursery, @@ -87,7 +97,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub REGEX_CREATION_IN_LOOPS, perf, "regular expression compilation performed in a loop" diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index c690696aefc1..597bfddecbc5 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -282,9 +282,9 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } { let mut apa = AuxParamsAttr { - first_bind_ident: ident, first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, + first_bind_ident: ident, first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -395,8 +395,8 @@ impl Default for AuxParamsAttr { counter: 0, has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, - first_bind_ident: Ident::empty(), first_block_span: DUMMY_SP, + first_bind_ident: Ident::empty(), first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, last_bind_ident: Ident::empty(), diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 8c8f11569c8c..d2d693eaa1f3 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -203,14 +203,18 @@ impl SlowVectorInit { "len", ); - span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { - diag.span_suggestion( - vec_alloc.allocation_expr.span.source_callsite(), - "consider replacing this with", - format!("vec![0; {len_expr}]"), - Applicability::Unspecified, - ); - }); + let span_to_replace = slow_fill + .span + .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + span_lint_and_sugg( + cx, + SLOW_VECTOR_INITIALIZATION, + span_to_replace, + msg, + "consider replacing this with", + format!("vec![0; {len_expr}]"), + Applicability::Unspecified, + ); } } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 78f0b7d121c2..ff1168005123 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -384,9 +384,9 @@ impl<'tcx> IndexBinding<'_, 'tcx> { fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool { let mut v = IndexBindingVisitor { - found_used: false, - suggest_span: self.suggest_span, idx: idx_ident, + suggest_span: self.suggest_span, + found_used: false, }; for stmt in self.block.stmts { diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index a1d92c3ac71f..82cc5155380e 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::has_repr_attr; +use clippy_utils::{has_repr_attr, is_in_test}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -37,7 +37,10 @@ declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]); impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) { + if is_struct_with_trailing_zero_sized_array(cx, item) + && !has_repr_attr(cx, item.hir_id()) + && !is_in_test(cx.tcx, item.hir_id()) + { span_lint_and_help( cx, TRAILING_EMPTY_ARRAY, diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 48d65eb15d9a..26323af31228 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -30,17 +30,14 @@ pub(super) fn check<'tcx>( | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::OrderedFields(Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // ptr <-> ptr @@ -50,7 +47,6 @@ pub(super) fn check<'tcx>( { from_ty = from_sub_ty; to_ty = to_sub_ty; - continue; }, // fat ptr <-> (*size, *size) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 363aea8be72e..43cce625c641 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -388,8 +388,8 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl, CheckTyContext { is_in_trait_impl, - is_exported, in_body: matches!(fn_kind, FnKind::Closure), + is_exported, ..CheckTyContext::default() }); } diff --git a/clippy_lints/src/unnecessary_literal_bound.rs b/clippy_lints/src/unnecessary_literal_bound.rs index 8165a45bc5ba..9f107fbeec03 100644 --- a/clippy_lints/src/unnecessary_literal_bound.rs +++ b/clippy_lints/src/unnecessary_literal_bound.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.84.0"] pub UNNECESSARY_LITERAL_BOUND, pedantic, "detects &str that could be &'static str in function return types" diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index c899b1868a6c..d00bd7f2b3db 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -120,8 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let mut visitor = AsyncFnVisitor { cx, found_await: false, - async_depth: 0, await_in_async_block: None, + async_depth: 0, }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { @@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // The actual linting happens in `check_crate_post`, once we've found all // uses of local async functions that do require asyncness to pass typeck self.unused_async_fns.push(UnusedAsyncFn { - await_in_async_block: visitor.await_in_async_block, - fn_span: span, def_id, + fn_span: span, + await_in_async_block: visitor.await_in_async_block, }); } } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 89bb429e2656..eaa119b045f1 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -245,9 +245,9 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { let mut delegate = MutationVisitor { - tcx: self.cx.tcx, is_mutated: false, local_id: unwrap_info.local_id, + tcx: self.cx.tcx, }; let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); @@ -397,8 +397,8 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { } let mut v = UnwrappableVariablesVisitor { - cx, unwrappables: Vec::new(), + cx, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index a5fad68eea18..08c178ed229f 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -73,6 +73,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Slice, SimplifiedType::Str, SimplifiedType::Bool, + SimplifiedType::Char, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter()) diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index ef1c46154d29..0730b561bc29 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -8,7 +8,7 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; -use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method}; +use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -132,9 +132,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { for (span, lint_opt) in &self.span_to_lint_map { if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc(),); + let help_msg = format!("you can use {} directly", suggest_slice.desc()); span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - diag.span_suggestion(*span, help_msg, snippet, *applicability); + // If the `vec!` macro contains comment, better not make the suggestion machine + // applicable as it would remove them. + let applicability = if *applicability != Applicability::Unspecified + && let source_map = cx.tcx.sess.source_map() + && span_contains_comment(source_map, *span) + { + Applicability::Unspecified + } else { + *applicability + }; + diag.span_suggestion(*span, help_msg, snippet, applicability); }); } } diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index cbc6885ae5de..d87d554eb074 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { local_id: id, init, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), + name: name.name, err_span: local.span, found: 0, last_push_expr: init_expr.hir_id, @@ -206,8 +206,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { && name.ident.as_str() == "push" { self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, err_span: searcher.err_span.to(stmt.span), + found: searcher.found + 1, last_push_expr: expr.hir_id, ..searcher }); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a42ddcdae353..31ae002e47d9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -248,8 +248,8 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests: conf.allow_print_in_tests, in_debug_impl: false, + allow_print_in_tests: conf.allow_print_in_tests, } } } diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index a702e0785a96..4df34891a2b1 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -300,8 +300,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus }; let mut vis = ExitPointFinder { - cx, state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + cx, }; if let Break(ExitCallFound) = vis.visit_block(block) { // Visitor found an unconditional `exit()` call, so don't lint. diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 945827c98c17..7fa070cd226b 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" # begin autogenerated version -version = "0.1.85" +version = "0.1.86" # end autogenerated version edition = "2021" description = "Helpful tools for writing lints, provided as they are used in Clippy" diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 73fefbcd5705..c267b804124a 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2024-12-26 +nightly-2025-01-09 ``` diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 4e12577b6df6..60be7e4a4d39 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -196,8 +196,8 @@ impl<'hir> IfOrIfLet<'hir> { if let ExprKind::DropTemps(new_cond) = cond.kind { return Some(Self { cond: new_cond, - r#else, then, + r#else, }); } if let ExprKind::Let(..) = cond.kind { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index d1d0abd46909..a1c48d5c36cf 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -806,8 +806,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - path_check: PathCheck::default(), s: FxHasher::default(), + path_check: PathCheck::default(), } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d42e40acbc08..eecfc3fb13f8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1243,9 +1243,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' let mut v = V { cx, - allow_closure: true, loops: Vec::new(), locals: HirIdSet::default(), + allow_closure: true, captures: HirIdMap::default(), }; v.visit_expr(expr); diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 17e6558a41c4..cf73bae2583b 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -32,8 +32,8 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { ) -> Self { Self { possible_borrower: TransitiveRelation::default(), - cx, body, + cx, possible_origin, } } diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 98bcedecccc6..2169a5fdd63b 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -130,7 +130,7 @@ impl Msrv { let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); if let Some(msrv_attr) = msrv_attrs.next() { - if let Some(duplicate) = msrv_attrs.last() { + if let Some(duplicate) = msrv_attrs.next_back() { sess.dcx() .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times") .with_span_note(msrv_attr.span(), "first definition found here") diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 8cb8cd590140..f15fffc09e8d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -33,6 +33,8 @@ pub const CHILD: [&str; 3] = ["std", "process", "Child"]; pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; +pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; +pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"]; // Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs index e38036315c2c..dcc1ec339ef9 100644 --- a/lintcheck/src/output.rs +++ b/lintcheck/src/output.rs @@ -94,8 +94,8 @@ impl ClippyWarning { Some(Self { name, diag, - url, krate: krate.to_string(), + url, }) } diff --git a/rust-toolchain b/rust-toolchain index 1000d90f52a5..b1f0a82b1f47 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2024-12-26" +channel = "nightly-2025-01-09" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index b8e0413e97bc..e2e4d92df79f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -574,12 +574,12 @@ impl LintMetadata { id_location: None, group: "deprecated", level: "none", - version, docs: format!( "### What it does\n\n\ Nothing. This lint has been deprecated\n\n\ ### Deprecation reason\n\n{reason}.\n", ), + version, applicability: Applicability::Unspecified, } } diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index a8225d037e82..64eba5e0888a 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -59,7 +59,7 @@ fn explore_directory(dir: &Path) -> Vec { missing_files.push(path.to_str().unwrap().to_string()); } }, - _ => continue, + _ => {}, }; } } diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index b99e8c0e76f0..ff178924bd15 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,3 +1,4 @@ + thread '' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: Would you like some help with that? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml new file mode 100644 index 000000000000..f43c9d97e825 --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -0,0 +1 @@ +lint-inconsistent-struct-field-initializers = true diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed new file mode 100644 index 000000000000..8092e40ff9f1 --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { x, y, z: z }; + + Foo { + x, + z: z, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + macro_unsafe_blocks: Vec::new(), + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + a: 3, + b: 2, + #[cfg(all())] + c: 1, + d: 0, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + a: 3, + #[cfg(any())] + c: 1, + b: 2, + d: 0, + }; + } +} diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs new file mode 100644 index 000000000000..cd1aff966528 --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { y, x, z: z }; + + Foo { + z: z, + x, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(all())] + c: 1, + b: 2, + a: 3, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(any())] + c: 1, + b: 2, + a: 3, + }; + } +} diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr new file mode 100644 index 000000000000..d2533960b84c --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr @@ -0,0 +1,77 @@ +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:18:11 + | +LL | Foo { y, x, z: z }; + | ^^^^^^^^^^ help: if the field evaluation order doesn't matter, try: `x, y, z: z` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:21:9 + | +LL | / z: z, +LL | | x, + | |_________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ x, +LL ~ z: z, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:36:13 + | +LL | / #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL | | expn_depth: if condition { 1 } else { 0 }, +LL | | macro_unsafe_blocks: Vec::new(), + | |___________________________________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ macro_unsafe_blocks: Vec::new(), +LL + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL ~ expn_depth: if condition { 1 } else { 0 }, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:55:13 + | +LL | / d: 0, +LL | | #[cfg(all())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + b: 2, +LL + #[cfg(all())] +LL + c: 1, +LL ~ d: 0, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:72:13 + | +LL | / d: 0, +LL | | #[cfg(any())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + #[cfg(any())] +LL + c: 1, +LL + b: 2, +LL ~ d: 0, + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 200129da25f5..01e9f5c26a37 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -46,6 +46,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -134,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -222,6 +224,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index 289a5ef38b8d..5365f3dd443c 100644 --- a/tests/ui/borrow_as_ptr.fixed +++ b/tests/ui/borrow_as_ptr.fixed @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = std::ptr::addr_of_mut!(val_mut); + + let mut x: [i32; 2] = [42, 43]; + let _raw = std::ptr::addr_of_mut!(x[1]).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&raw mut x[1]).wrapping_offset(-1); } diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs index b5328cb22dcd..261894f1341c 100644 --- a/tests/ui/borrow_as_ptr.rs +++ b/tests/ui/borrow_as_ptr.rs @@ -16,4 +16,12 @@ fn main() { let mut val_mut = 1; let _p_mut = &mut val_mut as *mut i32; + + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); +} + +fn issue_13882() { + let mut x: [i32; 2] = [42, 43]; + let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); } diff --git a/tests/ui/borrow_as_ptr.stderr b/tests/ui/borrow_as_ptr.stderr index ea618b06e2c8..4595fa4f2487 100644 --- a/tests/ui/borrow_as_ptr.stderr +++ b/tests/ui/borrow_as_ptr.stderr @@ -13,5 +13,17 @@ error: borrow as raw pointer LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` -error: aborting due to 2 previous errors +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:21:16 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(x[1])` + +error: borrow as raw pointer + --> tests/ui/borrow_as_ptr.rs:26:17 + | +LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]` + +error: aborting due to 4 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index de220505c3e0..a49d53fbbd38 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -47,6 +47,17 @@ impl std::ops::Deref for StaticRef { } } +// ICE regression test +mod issue12979 { + use std::cell::UnsafeCell; + + const ATOMIC_TUPLE: (Vec>, ()) = (Vec::new(), ()); + + fn main() { + let _x = &ATOMIC_TUPLE.0; + } +} + // use a tuple to make sure referencing a field behind a pointer isn't linted. const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 9a9028c86498..4cefcc28008d 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:54:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); | ^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::borrow_interior_mutable_const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:55:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); | ^^^^^^ @@ -20,7 +20,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:58:22 + --> tests/ui/borrow_interior_mutable_const/others.rs:69:22 | LL | let _once_ref = &ONCE_INIT; | ^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _once_ref = &ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:59:25 + --> tests/ui/borrow_interior_mutable_const/others.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; | ^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:60:27 + --> tests/ui/borrow_interior_mutable_const/others.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; | ^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:61:26 + --> tests/ui/borrow_interior_mutable_const/others.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; | ^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _once_mut = &mut ONCE_INIT; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:72:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; | ^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _ = &ATOMIC_TUPLE; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:73:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; | ^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _ = &ATOMIC_TUPLE.0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:74:19 + --> tests/ui/borrow_interior_mutable_const/others.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; | ^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:75:14 + --> tests/ui/borrow_interior_mutable_const/others.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:76:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); | ^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:81:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:92:13 | LL | let _ = ATOMIC_TUPLE.0[0]; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:86:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:97:5 | LL | CELL.set(2); | ^^^^ @@ -108,7 +108,7 @@ LL | CELL.set(2); = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/others.rs:87:16 + --> tests/ui/borrow_interior_mutable_const/others.rs:98:16 | LL | assert_eq!(CELL.get(), 6); | ^^^^ diff --git a/tests/ui/crashes/ice-10972-tait.rs b/tests/ui/crashes/ice-10972-tait.rs new file mode 100644 index 000000000000..f3ab9cebb7c2 --- /dev/null +++ b/tests/ui/crashes/ice-10972-tait.rs @@ -0,0 +1,9 @@ +// ICE: #10972 +// asked to assemble constituent types of unexpected type: Binder(Foo, []) +#![feature(type_alias_impl_trait)] + +use std::fmt::Debug; +type Foo = impl Debug; +const FOO2: Foo = 22_u32; + +pub fn main() {} diff --git a/tests/ui/crashes/ice-13862.rs b/tests/ui/crashes/ice-13862.rs new file mode 100644 index 000000000000..a5f010054b2f --- /dev/null +++ b/tests/ui/crashes/ice-13862.rs @@ -0,0 +1,19 @@ +#![crate_type = "lib"] +#![no_std] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +pub struct S; + +impl Future for S { + type Output = (); + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + todo!() + } +} + +pub fn f() -> S { + S +} diff --git a/tests/ui/double_ended_iterator_last.fixed b/tests/ui/double_ended_iterator_last.fixed new file mode 100644 index 000000000000..06c48e337537 --- /dev/null +++ b/tests/ui/double_ended_iterator_last.fixed @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').next_back() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = DeIterator.next_back(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + fn last(self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/tests/ui/double_ended_iterator_last.rs b/tests/ui/double_ended_iterator_last.rs new file mode 100644 index 000000000000..9c13b496d117 --- /dev/null +++ b/tests/ui/double_ended_iterator_last.rs @@ -0,0 +1,53 @@ +#![warn(clippy::double_ended_iterator_last)] + +// Typical case +pub fn last_arg(s: &str) -> Option<&str> { + s.split(' ').last() +} + +fn main() { + // General case + struct DeIterator; + impl Iterator for DeIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for DeIterator { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = DeIterator.last(); + // Should not apply to other methods of Iterator + let _ = DeIterator.count(); + + // Should not apply to simple iterators + struct SimpleIterator; + impl Iterator for SimpleIterator { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + } + let _ = SimpleIterator.last(); + + // Should not apply to custom implementations of last() + struct CustomLast; + impl Iterator for CustomLast { + type Item = (); + fn next(&mut self) -> Option { + Some(()) + } + fn last(self) -> Option { + Some(()) + } + } + impl DoubleEndedIterator for CustomLast { + fn next_back(&mut self) -> Option { + Some(()) + } + } + let _ = CustomLast.last(); +} diff --git a/tests/ui/double_ended_iterator_last.stderr b/tests/ui/double_ended_iterator_last.stderr new file mode 100644 index 000000000000..b795c18a736e --- /dev/null +++ b/tests/ui/double_ended_iterator_last.stderr @@ -0,0 +1,17 @@ +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:5:18 + | +LL | s.split(' ').last() + | ^^^^^^ help: try: `next_back()` + | + = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]` + +error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator + --> tests/ui/double_ended_iterator_last.rs:22:24 + | +LL | let _ = DeIterator.last(); + | ^^^^^^ help: try: `next_back()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 4c324587c96f..67bd3e4d2797 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -60,7 +60,11 @@ mod with_base { let z = 1; // Should lint. - Foo { x, z, ..Default::default() }; + Foo { + x, + z, + ..Default::default() + }; // Should NOT lint because the order is consistent with the definition. Foo { diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index 97bb7c789a72..c145eb2a239e 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,21 +1,24 @@ error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:36:9 + --> tests/ui/inconsistent_struct_constructor.rs:36:15 | LL | Foo { y, x, z }; - | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | ^^^^^^^ help: try: `x, y, z` | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:63:9 + --> tests/ui/inconsistent_struct_constructor.rs:64:13 | -LL | / Foo { -LL | | z, +LL | / z, LL | | x, -LL | | ..Default::default() -LL | | }; - | |_________^ help: try: `Foo { x, z, ..Default::default() }` + | |_____________^ + | +help: try + | +LL ~ x, +LL ~ z, + | error: aborting due to 2 previous errors diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index da95ba04b821..178e300ff5bf 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::double_ended_iterator_last)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 7d8a584b0224..d7d3d299349e 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 58c374ab8cd1..45e1349febd0 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,5 +1,10 @@ #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code, clippy::let_unit_value, clippy::useless_vec)] +#![allow( + dead_code, + clippy::let_unit_value, + clippy::useless_vec, + clippy::double_ended_iterator_last +)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 7a822a79494b..e6680266f107 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -1,5 +1,5 @@ error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:7:29 + --> tests/ui/iter_overeager_cloned.rs:12:29 | LL | let _: Option = vec.iter().cloned().last(); | ^^^^^^^^^^---------------- @@ -10,7 +10,7 @@ LL | let _: Option = vec.iter().cloned().last(); = help: to override `-D warnings` add `#[allow(clippy::iter_overeager_cloned)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:9:29 + --> tests/ui/iter_overeager_cloned.rs:14:29 | LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -18,7 +18,7 @@ LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next(); | help: try: `.next().cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:11:20 + --> tests/ui/iter_overeager_cloned.rs:16:20 | LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------- @@ -29,7 +29,7 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:13:21 + --> tests/ui/iter_overeager_cloned.rs:18:21 | LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | ^^^^^^^^^^----------------- @@ -37,7 +37,7 @@ LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); | help: try: `.take(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:15:21 + --> tests/ui/iter_overeager_cloned.rs:20:21 | LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | ^^^^^^^^^^----------------- @@ -45,7 +45,7 @@ LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); | help: try: `.skip(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:17:13 + --> tests/ui/iter_overeager_cloned.rs:22:13 | LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------- @@ -53,7 +53,7 @@ LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); | help: try: `.nth(2).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:19:13 + --> tests/ui/iter_overeager_cloned.rs:24:13 | LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] | _____________^ @@ -69,7 +69,7 @@ LL ~ .flatten().cloned(); | error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:24:13 + --> tests/ui/iter_overeager_cloned.rs:29:13 | LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | ^^^^^^^^^^---------------------------------------- @@ -77,7 +77,7 @@ LL | let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); | help: try: `.filter(|&x| x.starts_with('2')).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:26:13 + --> tests/ui/iter_overeager_cloned.rs:31:13 | LL | let _ = vec.iter().cloned().find(|x| x == "2"); | ^^^^^^^^^^---------------------------- @@ -85,7 +85,7 @@ LL | let _ = vec.iter().cloned().find(|x| x == "2"); | help: try: `.find(|&x| x == "2").cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:30:17 + --> tests/ui/iter_overeager_cloned.rs:35:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -93,7 +93,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:31:17 + --> tests/ui/iter_overeager_cloned.rs:36:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -101,7 +101,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:37:17 + --> tests/ui/iter_overeager_cloned.rs:42:17 | LL | let _ = vec.iter().cloned().filter(f); | ^^^^^^^^^^------------------- @@ -109,7 +109,7 @@ LL | let _ = vec.iter().cloned().filter(f); | help: try: `.filter(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:38:17 + --> tests/ui/iter_overeager_cloned.rs:43:17 | LL | let _ = vec.iter().cloned().find(f); | ^^^^^^^^^^----------------- @@ -117,7 +117,7 @@ LL | let _ = vec.iter().cloned().find(f); | help: try: `.find(|&x| f(x)).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:45:9 + --> tests/ui/iter_overeager_cloned.rs:50:9 | LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | ^^^^------------------------------------------------------- @@ -125,7 +125,7 @@ LL | iter.cloned().filter(move |(&a, b)| a == 1 && b == &target) | help: try: `.filter(move |&(&a, b)| a == 1 && b == &target).cloned()` error: unnecessarily eager cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:56:13 + --> tests/ui/iter_overeager_cloned.rs:61:13 | LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target) | ^^^^------------------------------------------------------------ @@ -133,7 +133,7 @@ LL | iter.cloned().filter(move |S { a, b }| **a == 1 && b == &target | help: try: `.filter(move |&S { a, b }| **a == 1 && b == &target).cloned()` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:60:13 + --> tests/ui/iter_overeager_cloned.rs:65:13 | LL | let _ = vec.iter().cloned().map(|x| x.len()); | ^^^^^^^^^^-------------------------- @@ -141,7 +141,7 @@ LL | let _ = vec.iter().cloned().map(|x| x.len()); | help: try: `.map(|x| x.len())` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:65:13 + --> tests/ui/iter_overeager_cloned.rs:70:13 | LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | ^^^^^^^^^^---------------------------------------------- @@ -149,7 +149,7 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | help: try: `.for_each(|x| assert!(!x.is_empty()))` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:67:13 + --> tests/ui/iter_overeager_cloned.rs:72:13 | LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- @@ -157,7 +157,7 @@ LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); | help: try: `.all(|x| x.len() == 1)` error: unneeded cloning of iterator items - --> tests/ui/iter_overeager_cloned.rs:69:13 + --> tests/ui/iter_overeager_cloned.rs:74:13 | LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); | ^^^^^^^^^^------------------------------- diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 27319d9c20e8..c9c476ba4214 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", (**d2s).is_empty()); + println!("{}", std::borrow::Cow::Borrowed("").is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (!has_is_empty.is_empty()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 03c05bc6ed7b..610a5448d10e 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -108,6 +108,8 @@ fn main() { let d2s = DerefToDerefToString {}; println!("{}", &**d2s == ""); + println!("{}", std::borrow::Cow::Borrowed("") == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. @@ -226,3 +228,23 @@ fn binop_with_macros() { (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); } + +fn no_infinite_recursion() -> bool { + struct S; + + impl Deref for S { + type Target = Self; + fn deref(&self) -> &Self::Target { + self + } + } + + impl PartialEq<&'static str> for S { + fn eq(&self, _other: &&'static str) -> bool { + false + } + } + + // Do not crash while checking if S implements `.is_empty()` + S == "" +} diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr index 5c849a2aca64..8d6b57e4b6d4 100644 --- a/tests/ui/len_zero.stderr +++ b/tests/ui/len_zero.stderr @@ -58,107 +58,113 @@ error: comparison to empty slice LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` +error: comparison to empty slice + --> tests/ui/len_zero.rs:111:20 + | +LL | println!("{}", std::borrow::Cow::Borrowed("") == ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `std::borrow::Cow::Borrowed("").is_empty()` + error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:126:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:129:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:132:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:133:8 + --> tests/ui/len_zero.rs:135:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:136:8 + --> tests/ui/len_zero.rs:138:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:149:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:152:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:155:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:156:8 + --> tests/ui/len_zero.rs:158:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:159:8 + --> tests/ui/len_zero.rs:161:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:173:8 + --> tests/ui/len_zero.rs:175:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:185:6 + --> tests/ui/len_zero.rs:187:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:186:6 + --> tests/ui/len_zero.rs:188:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:190:8 + --> tests/ui/len_zero.rs:192:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:224:8 + --> tests/ui/len_zero.rs:226:8 | LL | if has_is_empty.len() == compare_to!(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:225:8 + --> tests/ui/len_zero.rs:227:8 | LL | if has_is_empty.len() == zero!() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:227:6 + --> tests/ui/len_zero.rs:229:6 | LL | (compare_to!(0) < has_is_empty.len()).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index e7801f7376aa..1fb1df5b4425 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index 2de74c7eaa88..4f6d38f0d145 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -28,3 +28,25 @@ fn main() { let _ = (7_u32 as i32 + (y_i - 1)) / y_i; let _ = (7_u32 as i32 + (4 - 1)) / 4; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index dc652dff405f..3d87fe8e0409 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -31,5 +31,59 @@ error: manually reimplementing `div_ceil` LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` -error: aborting due to 5 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:34:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:37:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:40:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:43:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:45:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:46:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:47:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:49:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/manual_div_ceil_with_feature.fixed b/tests/ui/manual_div_ceil_with_feature.fixed index a1d678c66898..f32b78aa14d0 100644 --- a/tests/ui/manual_div_ceil_with_feature.fixed +++ b/tests/ui/manual_div_ceil_with_feature.fixed @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = 2048_usize.div_ceil(x); + + let x = 5usize; + let _ = 2048usize.div_ceil(x); + + let x = 5usize; + let _ = 2048_usize.div_ceil(x); + + let x = 2048usize; + let _ = x.div_ceil(4); + + let _ = 2048_i32.div_ceil(4); + + let _: u32 = 2048_u32.div_ceil(6); + let _: usize = 2048_usize.div_ceil(6); + let _: u32 = 0x2048_u32.div_ceil(0x6); + + let _ = 2048_u32.div_ceil(6u32); + + let x = -2; + let _ = (-2048_i32).div_ceil(x); + + let _ = 1_000_000_u32.div_ceil(6u32); +} diff --git a/tests/ui/manual_div_ceil_with_feature.rs b/tests/ui/manual_div_ceil_with_feature.rs index 58cb1dbe34d1..54d89fcbd462 100644 --- a/tests/ui/manual_div_ceil_with_feature.rs +++ b/tests/ui/manual_div_ceil_with_feature.rs @@ -23,3 +23,30 @@ fn main() { let _ = (x + (y - 1)) / z; } + +fn issue_13843() { + let x = 3usize; + let _ = (2048 + x - 1) / x; + + let x = 5usize; + let _ = (2048usize + x - 1) / x; + + let x = 5usize; + let _ = (2048_usize + x - 1) / x; + + let x = 2048usize; + let _ = (x + 4 - 1) / 4; + + let _ = (2048 + 4 - 1) / 4; + + let _: u32 = (2048 + 6 - 1) / 6; + let _: usize = (2048 + 6 - 1) / 6; + let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + + let _ = (2048 + 6u32 - 1) / 6u32; + + let x = -2; + let _ = (-2048 + x - 1) / x; + + let _ = (1_000_000 + 6u32 - 1) / 6u32; +} diff --git a/tests/ui/manual_div_ceil_with_feature.stderr b/tests/ui/manual_div_ceil_with_feature.stderr index 361ef9bd9f42..c5e8c1a687cd 100644 --- a/tests/ui/manual_div_ceil_with_feature.stderr +++ b/tests/ui/manual_div_ceil_with_feature.stderr @@ -43,5 +43,71 @@ error: manually reimplementing `div_ceil` LL | let _ = (z_u + (4 - 1)) / 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` -error: aborting due to 7 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:29:13 + | +LL | let _ = (2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:32:13 + | +LL | let _ = (2048usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:35:13 + | +LL | let _ = (2048_usize + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:38:13 + | +LL | let _ = (x + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:40:13 + | +LL | let _ = (2048 + 4 - 1) / 4; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:42:18 + | +LL | let _: u32 = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:43:20 + | +LL | let _: usize = (2048 + 6 - 1) / 6; + | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_usize.div_ceil(6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:44:18 + | +LL | let _: u32 = (0x2048 + 0x6 - 1) / 0x6; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `0x2048_u32.div_ceil(0x6)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:46:13 + | +LL | let _ = (2048 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `2048_u32.div_ceil(6u32)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:49:13 + | +LL | let _ = (-2048 + x - 1) / x; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(-2048_i32).div_ceil(x)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:51:13 + | +LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` + +error: aborting due to 18 previous errors diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index a72caa3a37ee..179149f697db 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -82,3 +82,8 @@ fn generics() { take_while(|c: u8| c.is_ascii_uppercase()); take_while(|c: char| c.is_ascii_uppercase()); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index bb6e2a317da1..74f35ce94e84 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -82,3 +82,8 @@ fn generics() { take_while(|c| (b'A'..=b'Z').contains(&c)); take_while(|c: char| ('A'..='Z').contains(&c)); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index a93ccace28a6..92d93208006a 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -173,5 +173,27 @@ error: manual check for common ascii range LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` -error: aborting due to 27 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:87:63 + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:88:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 76916d465919..eafc8b6e81ca 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -55,6 +55,18 @@ fn long_span() { .collect(); } +#[allow(clippy::useless_vec)] +fn no_suggestion_if_comments_present() { + let vec = vec![vec![1, 2, 3]]; + let _ = vec + .iter() + // a lovely comment explaining the code in very detail + .map(|x| x.iter()) + //~^ ERROR: called `map(..).flatten()` on `Iterator` + // the answer to life, the universe and everything could be here + .flatten(); +} + fn main() { long_span(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index a5837b97617d..34bd174d7dde 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -102,5 +102,14 @@ LL + } LL + }) | -error: aborting due to 4 previous errors +error: called `map(..).flatten()` on `Iterator` + --> tests/ui/map_flatten.rs:64:10 + | +LL | .map(|x| x.iter()) + | __________^ +... | +LL | | .flatten(); + | |__________________^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| x.iter())` + +error: aborting due to 5 previous errors diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index 53ebfb40ba0d..3257ddc6f72b 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -61,3 +61,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied(); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.next(); +} diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index c646c0568595..be3bb9a4f106 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -65,3 +65,18 @@ fn issue11764() { // no match ergonomics for `(i32, i32)` let _ = x.iter().copied().map(|(x, y)| (x, y)); } + +fn issue13904() { + // don't lint: `it.next()` would not be legal as `it` is immutable + let it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + #[allow(unused_mut)] + let mut it = [1, 2, 3].into_iter(); + let _ = it.map(|x| x).next(); + + // lint + let it = [1, 2, 3].into_iter(); + let _ = { it }.map(|x| x).next(); +} diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index 0a0dc9c8f075..aa3fc4ae0b5c 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -73,5 +73,17 @@ error: unnecessary map of the identity function LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 11 previous errors +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:77:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:81:19 + | +LL | let _ = { it }.map(|x| x).next(); + | ^^^^^^^^^^^ help: remove the call to `map` + +error: aborting due to 13 previous errors diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index ca323dcf1733..d2f9e34a5ceb 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -47,7 +47,34 @@ fn get_y() -> u32 { Y } -// Don't lint entrypoint functions +#[cfg(test)] +mod with_test_fn { + #[derive(Clone, Copy)] + pub struct Foo { + pub n: u32, + } + + impl Foo { + #[must_use] + pub const fn new(n: u32) -> Foo { + Foo { n } + } + } + + #[test] + fn foo_is_copy() { + let foo = Foo::new(42); + let one = foo; + let two = foo; + _ = one; + _ = two; + } +} + +// Allowing on this function, because it would lint, which we don't want in this case. +// if we have `#[start]` and `#[test]` check `is_entrypoint_fn(cx, def_id.to_def_id())` is stopped +// working +#[allow(clippy::missing_const_for_fn)] #[start] fn init(num: isize, something: *const *const u8) -> isize { 1 diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index 9da60c687d44..530eb77d83d2 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(&'r#struct self) {} + fn f2(&'r#struct mut self) {} +} + fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index fc4ec5cb0b3c..5a1ff96a11cc 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -64,4 +64,9 @@ impl ValType { } } +trait Foo<'r#struct> { + fn f1(self: &'r#struct Self) {} + fn f2(self: &'r#struct mut Self) {} +} + fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index c653267f7525..7ebbbaa122f5 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -37,5 +37,17 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: aborting due to 6 previous errors +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:68:11 + | +LL | fn f1(self: &'r#struct Self) {} + | ^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct self` + +error: the type of the `self` parameter does not need to be arbitrary + --> tests/ui/needless_arbitrary_self_type.rs:69:11 + | +LL | fn f2(self: &'r#struct mut Self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct mut self` + +error: aborting due to 8 previous errors diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index b6d8a8f61aeb..334a2b32775f 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -87,6 +87,14 @@ fn simple_loop4() { } } +fn simple_loop5() { + loop { + println!("bleh"); + { continue } + //~^ ERROR: this `continue` expression is redundant + } +} + mod issue_2329 { fn condition() -> bool { unimplemented!() @@ -168,3 +176,60 @@ fn issue_13641() { } } } + +mod issue_4077 { + fn main() { + 'outer: loop { + 'inner: loop { + do_something(); + if some_expr() { + println!("bar-7"); + continue 'outer; + } else if !some_expr() { + println!("bar-8"); + continue 'inner; + } else { + println!("bar-9"); + continue 'inner; + } + } + } + + for _ in 0..10 { + match "foo".parse::() { + Ok(_) => do_something(), + Err(_) => { + println!("bar-10"); + continue; + }, + } + } + + loop { + if true { + } else { + // redundant `else` + continue; // redundant `continue` + } + } + + loop { + if some_expr() { + continue; + } else { + do_something(); + } + } + } + + // The contents of these functions are irrelevant, the purpose of this file is + // shown in main. + + fn do_something() { + std::process::exit(0); + } + + fn some_expr() -> bool { + true + } +} diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 0741ba692487..ec39d6234195 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -63,7 +63,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:60:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -71,7 +71,7 @@ error: this `continue` expression is redundant --> tests/ui/needless_continue.rs:68:9 | LL | continue; - | ^^^^^^^^^ + | ^^^^^^^^ | = help: consider dropping the `continue` expression @@ -91,8 +91,16 @@ LL | continue | = help: consider dropping the `continue` expression +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:93:11 + | +LL | { continue } + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + error: this `else` block is redundant - --> tests/ui/needless_continue.rs:136:24 + --> tests/ui/needless_continue.rs:144:24 | LL | } else { | ________________________^ @@ -117,7 +125,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> tests/ui/needless_continue.rs:143:17 + --> tests/ui/needless_continue.rs:151:17 | LL | / if condition() { LL | | @@ -137,12 +145,70 @@ LL | | } } error: this `continue` expression is redundant - --> tests/ui/needless_continue.rs:166:13 + --> tests/ui/needless_continue.rs:174:13 | LL | continue 'b; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: consider dropping the `continue` expression -error: aborting due to 9 previous errors +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:190:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:193:21 + | +LL | continue 'inner; + | ^^^^^^^^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `continue` expression is redundant + --> tests/ui/needless_continue.rs:203:21 + | +LL | continue; + | ^^^^^^^^ + | + = help: consider dropping the `continue` expression + +error: this `else` block is redundant + --> tests/ui/needless_continue.rs:210:20 + | +LL | } else { + | ____________________^ +LL | | // redundant `else` +LL | | continue; // redundant `continue` +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if true { + // merged code follows: + + } + +error: there is no need for an explicit `else` block for this `if` expression + --> tests/ui/needless_continue.rs:217:13 + | +LL | / if some_expr() { +LL | | continue; +LL | | } else { +LL | | do_something(); +LL | | } + | |_____________^ + | + = help: consider dropping the `else` clause + if some_expr() { + continue; + } + { + do_something(); + } + +error: aborting due to 15 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index c9b76262d70b..c7e0cd2610f0 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -137,3 +137,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = (*p).is_none(); + } +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index a5f9caf659c6..6d9a9f7f9428 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -164,3 +164,11 @@ fn issue10803() { // Don't lint let _ = matches!(x, Some(16)); } + +fn issue13902() { + let x = Some(0); + let p = &raw const x; + unsafe { + let _ = matches!(*p, None); + } +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 575f199be42c..34d80f5ca782 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -209,5 +209,11 @@ error: redundant pattern matching, consider using `is_none()` LL | let _ = matches!(x, None); | ^^^^^^^^^^^^^^^^^ help: try: `x.is_none()` -error: aborting due to 30 previous errors +error: redundant pattern matching, consider using `is_none()` + --> tests/ui/redundant_pattern_matching_option.rs:172:17 + | +LL | let _ = matches!(*p, None); + | ^^^^^^^^^^^^^^^^^^ help: try: `(*p).is_none()` + +error: aborting due to 31 previous errors diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 16f81019574f..2ba87f412500 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -11,22 +11,22 @@ fn extend_vector() { // Extend with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.extend(repeat(0).take(len)); //~^ ERROR: slow zero-filling initialization //~| NOTE: `-D clippy::slow-vector-initialization` implied by `-D warnings` + vec1.extend(repeat(0).take(len)); // Extend with len expression let mut vec2 = Vec::with_capacity(len - 10); - vec2.extend(repeat(0).take(len - 10)); //~^ ERROR: slow zero-filling initialization + vec2.extend(repeat(0).take(len - 10)); // Extend with mismatching expression should not be warned let mut vec3 = Vec::with_capacity(24322); vec3.extend(repeat(0).take(2)); let mut vec4 = Vec::with_capacity(len); - vec4.extend(repeat(0).take(vec4.capacity())); //~^ ERROR: slow zero-filling initialization + vec4.extend(repeat(0).take(vec4.capacity())); } fn mixed_extend_resize_vector() { @@ -36,20 +36,20 @@ fn mixed_extend_resize_vector() { // Slow initialization let mut resized_vec = Vec::with_capacity(30); - resized_vec.resize(30, 0); //~^ ERROR: slow zero-filling initialization + resized_vec.resize(30, 0); let mut extend_vec = Vec::with_capacity(30); - extend_vec.extend(repeat(0).take(30)); //~^ ERROR: slow zero-filling initialization + extend_vec.extend(repeat(0).take(30)); } fn resize_vector() { // Resize with constant expression let len = 300; let mut vec1 = Vec::with_capacity(len); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize mismatch len let mut vec2 = Vec::with_capacity(200); @@ -57,39 +57,39 @@ fn resize_vector() { // Resize with len expression let mut vec3 = Vec::with_capacity(len - 10); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); let mut vec4 = Vec::with_capacity(len); - vec4.resize(vec4.capacity(), 0); //~^ ERROR: slow zero-filling initialization + vec4.resize(vec4.capacity(), 0); // Reinitialization should be warned vec1 = Vec::with_capacity(10); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); } fn from_empty_vec() { // Resize with constant expression let len = 300; let mut vec1 = Vec::new(); - vec1.resize(len, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(len, 0); // Resize with len expression let mut vec3 = Vec::new(); - vec3.resize(len - 10, 0); //~^ ERROR: slow zero-filling initialization + vec3.resize(len - 10, 0); // Reinitialization should be warned vec1 = Vec::new(); - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); vec1 = vec![]; - vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + vec1.resize(10, 0); macro_rules! x { () => { diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index 353c677097be..7f4b9f7b67a4 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -1,109 +1,122 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:14:5 + --> tests/ui/slow_vector_initialization.rs:13:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.extend(repeat(0).take(len)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +... | +LL | | vec1.extend(repeat(0).take(len)); + | |____________________________________^ help: consider replacing this with: `vec![0; len]` | = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:20:5 + --> tests/ui/slow_vector_initialization.rs:19:20 | -LL | let mut vec2 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec2.extend(repeat(0).take(len - 10)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec2 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec2.extend(repeat(0).take(len - 10)); + | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:28:5 + --> tests/ui/slow_vector_initialization.rs:27:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.extend(repeat(0).take(vec4.capacity())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.extend(repeat(0).take(vec4.capacity())); + | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:39:5 + --> tests/ui/slow_vector_initialization.rs:38:27 | -LL | let mut resized_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | resized_vec.resize(30, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut resized_vec = Vec::with_capacity(30); + | ___________________________^ +LL | | +LL | | resized_vec.resize(30, 0); + | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:43:5 + --> tests/ui/slow_vector_initialization.rs:42:26 | -LL | let mut extend_vec = Vec::with_capacity(30); - | ---------------------- help: consider replacing this with: `vec![0; 30]` -LL | extend_vec.extend(repeat(0).take(30)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut extend_vec = Vec::with_capacity(30); + | __________________________^ +LL | | +LL | | extend_vec.extend(repeat(0).take(30)); + | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:51:5 + --> tests/ui/slow_vector_initialization.rs:50:20 | -LL | let mut vec1 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:60:5 + --> tests/ui/slow_vector_initialization.rs:59:20 | -LL | let mut vec3 = Vec::with_capacity(len - 10); - | ---------------------------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::with_capacity(len - 10); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:64:5 + --> tests/ui/slow_vector_initialization.rs:63:20 | -LL | let mut vec4 = Vec::with_capacity(len); - | ----------------------- help: consider replacing this with: `vec![0; len]` -LL | vec4.resize(vec4.capacity(), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec4 = Vec::with_capacity(len); + | ____________________^ +LL | | +LL | | vec4.resize(vec4.capacity(), 0); + | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:69:5 + --> tests/ui/slow_vector_initialization.rs:68:12 | -LL | vec1 = Vec::with_capacity(10); - | ---------------------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::with_capacity(10); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:77:5 + --> tests/ui/slow_vector_initialization.rs:76:20 | -LL | let mut vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len]` -LL | vec1.resize(len, 0); - | ^^^^^^^^^^^^^^^^^^^ +LL | let mut vec1 = Vec::new(); + | ____________________^ +LL | | +LL | | vec1.resize(len, 0); + | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:82:5 + --> tests/ui/slow_vector_initialization.rs:81:20 | -LL | let mut vec3 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; len - 10]` -LL | vec3.resize(len - 10, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let mut vec3 = Vec::new(); + | ____________________^ +LL | | +LL | | vec3.resize(len - 10, 0); + | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:87:5 + --> tests/ui/slow_vector_initialization.rs:86:12 | -LL | vec1 = Vec::new(); - | ---------- help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = Vec::new(); + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:91:5 + --> tests/ui/slow_vector_initialization.rs:90:12 | -LL | vec1 = vec![]; - | ------ help: consider replacing this with: `vec![0; 10]` -LL | vec1.resize(10, 0); - | ^^^^^^^^^^^^^^^^^^ +LL | vec1 = vec![]; + | ____________^ +LL | | +LL | | vec1.resize(10, 0); + | |______________________^ help: consider replacing this with: `vec![0; 10]` error: aborting due to 13 previous errors diff --git a/tests/ui/starts_ends_with.fixed b/tests/ui/starts_ends_with.fixed index 4a66ca7ec91a..252b6e5a98c0 100644 --- a/tests/ui/starts_ends_with.fixed +++ b/tests/ui/starts_ends_with.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/tests/ui/starts_ends_with.rs b/tests/ui/starts_ends_with.rs index 16a68e02d66d..6c5655f31782 100644 --- a/tests/ui/starts_ends_with.rs +++ b/tests/ui/starts_ends_with.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_if, dead_code, unused_must_use)] +#![allow(clippy::needless_if, dead_code, unused_must_use, clippy::double_ended_iterator_last)] fn main() {} diff --git a/tests/ui/trailing_empty_array.rs b/tests/ui/trailing_empty_array.rs index 309a5920dfde..ea3b8ff01afa 100644 --- a/tests/ui/trailing_empty_array.rs +++ b/tests/ui/trailing_empty_array.rs @@ -193,3 +193,17 @@ type C = ConstParamNoDefault<0>; type D = ConstParamNonZeroDefault<0>; fn main() {} + +#[cfg(test)] +mod tests { + pub struct Friend { + age: u8, + } + + #[test] + fn oldest_empty_is_none() { + struct Michael { + friends: [Friend; 0], + } + } +} diff --git a/tests/ui/unnecessary_map_or.fixed b/tests/ui/unnecessary_map_or.fixed index 70b78ceca502..efea28e7045c 100644 --- a/tests/ui/unnecessary_map_or.fixed +++ b/tests/ui/unnecessary_map_or.fixed @@ -3,15 +3,16 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; fn main() { // should trigger - let _ = (Some(5) == Some(5)); - let _ = (Some(5) != Some(5)); - let _ = (Some(5) == Some(5)); + let _ = Some(5) == Some(5); + let _ = Some(5) != Some(5); + let _ = Some(5) == Some(5); let _ = Some(5).is_some_and(|n| { let _ = n; 6 >= 5 @@ -21,10 +22,13 @@ fn main() { let _ = Some(5).is_some_and(|n| n == n); let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); - let _ = (Ok::(5) == Ok(5)); + let _ = Ok::(5) == Ok(5); let _ = (Some(5) == Some(5)).then(|| 1); let _ = Some(5).is_none_or(|n| n == 5); let _ = Some(5).is_none_or(|n| 5 == n); + let _ = !(Some(5) == Some(5)); + let _ = (Some(5) == Some(5)) || false; + let _ = (Some(5) == Some(5)) as usize; macro_rules! x { () => { @@ -60,7 +64,7 @@ fn main() { #[derive(PartialEq)] struct S2; let r: Result = Ok(4); - let _ = (r == Ok(8)); + let _ = r == Ok(8); // do not lint `Result::map_or(true, …)` let r: Result = Ok(4); diff --git a/tests/ui/unnecessary_map_or.rs b/tests/ui/unnecessary_map_or.rs index 507577159771..05a0ca816ef6 100644 --- a/tests/ui/unnecessary_map_or.rs +++ b/tests/ui/unnecessary_map_or.rs @@ -3,6 +3,7 @@ #![allow(clippy::no_effect)] #![allow(clippy::eq_op)] #![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::nonminimal_bool)] #[clippy::msrv = "1.70.0"] #[macro_use] extern crate proc_macros; @@ -28,6 +29,9 @@ fn main() { let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); let _ = Some(5).map_or(true, |n| n == 5); let _ = Some(5).map_or(true, |n| 5 == n); + let _ = !Some(5).map_or(false, |n| n == 5); + let _ = Some(5).map_or(false, |n| n == 5) || false; + let _ = Some(5).map_or(false, |n| n == 5) as usize; macro_rules! x { () => { diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr index 890abb012288..2b78996d5f3e 100644 --- a/tests/ui/unnecessary_map_or.stderr +++ b/tests/ui/unnecessary_map_or.stderr @@ -1,30 +1,30 @@ error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:12:13 + --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:13:13 + --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:14:13 + --> tests/ui/unnecessary_map_or.rs:15:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:18:13 + --> tests/ui/unnecessary_map_or.rs:19:13 | LL | let _ = Some(5).map_or(false, |n| { | _____________^ @@ -42,88 +42,106 @@ LL ~ }); | error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:22:13 + --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:23:13 + --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:24:13 + --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:25:13 + --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:26:13 + --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::, i32>(vec![5]).is_ok_and(|n| n == [5])` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:27:13 + --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::(5) == Ok(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::(5) == Ok(5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:28:13 + --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:29:13 + --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:30:13 + --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:54:13 + --> tests/ui/unnecessary_map_or.rs:32:14 + | +LL | let _ = !Some(5).map_or(false, |n| n == 5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:33:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) || false; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:34:13 + | +LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:59:13 + --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:60:13 + --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:61:13 + --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` error: this `map_or` can be simplified - --> tests/ui/unnecessary_map_or.rs:66:13 + --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` -error: aborting due to 18 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/useless_vec.rs b/tests/ui/useless_vec.rs new file mode 100644 index 000000000000..880809f81d7a --- /dev/null +++ b/tests/ui/useless_vec.rs @@ -0,0 +1,15 @@ +//@no-rustfix: no suggestions + +#![warn(clippy::useless_vec)] + +// Regression test for . +fn foo() { + // There should be no suggestion in this case. + let _some_variable = vec![ + //~^ useless_vec + 1, 2, // i'm here to stay + 3, 4, // but this one going away ;-; + ]; // that is life anyways +} + +fn main() {} diff --git a/tests/ui/useless_vec.stderr b/tests/ui/useless_vec.stderr new file mode 100644 index 000000000000..e47364fb06d3 --- /dev/null +++ b/tests/ui/useless_vec.stderr @@ -0,0 +1,21 @@ +error: useless use of `vec!` + --> tests/ui/useless_vec.rs:8:26 + | +LL | let _some_variable = vec![ + | __________________________^ +LL | | +LL | | 1, 2, // i'm here to stay +LL | | 3, 4, // but this one going away ;-; +LL | | ]; // that is life anyways + | |_____^ + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` +help: you can use an array directly + | +LL ~ let _some_variable = [1, 2, // i'm here to stay +LL ~ 3, 4]; // that is life anyways + | + +error: aborting due to 1 previous error + From db4aac6d21b0827aa0401b8ac31b34a9cf859f30 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Thu, 9 Jan 2025 11:33:48 -0800 Subject: [PATCH 021/342] Correct version of `literal_string_with_formatting_args` It claims to be in 1.83 but in fact will not be until 1.85. Evidence: is where it was merged into rust-lang/rust, and has a milestone of 1.85. --- clippy_lints/src/literal_string_with_formatting_args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/literal_string_with_formatting_args.rs b/clippy_lints/src/literal_string_with_formatting_args.rs index 49353a1b76be..d9de784873bc 100644 --- a/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -29,7 +29,7 @@ declare_clippy_lint! { /// let y = "hello"; /// x.expect(&format!("{y:?}")); /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.85.0"] pub LITERAL_STRING_WITH_FORMATTING_ARGS, suspicious, "Checks if string literals have formatting arguments" From d8301d762f423b2a08f67319a4178dc2c45bf934 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 4 Jan 2025 18:43:32 +0100 Subject: [PATCH 022/342] Do not intersect spans coming from different contexts The code should not attempt to obtain a snippet by capping the function signature span with its identifier span without checking that they are in the same context. --- clippy_lints/src/no_mangle_with_rust_abi.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/no_mangle_with_rust_abi.rs b/clippy_lints/src/no_mangle_with_rust_abi.rs index 9ee4e4932777..2e2916c957da 100644 --- a/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -37,7 +37,9 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind { + if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind + && !item.span.from_expansion() + { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut app = Applicability::MaybeIncorrect; let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app); From f3b1dd636d35b1b9debfdd52dc47a9960bb34948 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Fri, 10 Jan 2025 15:38:08 +0500 Subject: [PATCH 023/342] Fix labels --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f6c918fc6cc..3b33f7190638 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,7 +199,7 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and responding to issues there may not always be enough time to stay on top of it all. -Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example +Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example an ICE in a popular crate that many other crates depend on. We don't want Clippy to crash on your code and we want it to be as reliable as the suggestions from Rust compiler errors. @@ -213,8 +213,8 @@ Or rather: before the sync this should be addressed, e.g. by removing a lint again, so it doesn't hit beta/stable. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash -[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug +[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE +[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug [p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low [p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium [p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high From 7639e82939d2c80026c39dd0cf2f9be7ff6c0661 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 10 Jan 2025 11:31:16 +0100 Subject: [PATCH 024/342] CI: check the presence of the changelog line in every pull request Checking it only in the merge queue leads to deferred failures once the decision to merge has been taken already. --- .github/workflows/clippy_changelog.yml | 57 ++++++++++++++++++++++++++ .github/workflows/clippy_mq.yml | 34 +-------------- 2 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/clippy_changelog.yml diff --git a/.github/workflows/clippy_changelog.yml b/.github/workflows/clippy_changelog.yml new file mode 100644 index 000000000000..c08abd155176 --- /dev/null +++ b/.github/workflows/clippy_changelog.yml @@ -0,0 +1,57 @@ +name: Clippy changelog check + +on: + merge_group: + pull_request: + types: [opened, reopened, edited] + +concurrency: + # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. + # If the push is not attached to a PR, we will cancel all builds on the same branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + +jobs: + changelog: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + # Run + - name: Check Changelog + if: ${{ github.event_name == 'pull_request' }} + run: | + body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ + python -c "import sys, json; print(json.load(sys.stdin)['body'])") + output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || { + echo "ERROR: pull request message must contain 'changelog: ...'. Please add it." + exit 1 + } + echo "changelog: $output" + env: + PYTHONIOENCODING: 'utf-8' + PR_NUMBER: '${{ github.event.number }}' + + # We need to have the "conclusion" job also on PR CI, to make it possible + # to add PRs to a merge queue. + conclusion_changelog: + needs: [ changelog ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/.github/workflows/clippy_mq.yml b/.github/workflows/clippy_mq.yml index dee7d028655e..c337a96bdac5 100644 --- a/.github/workflows/clippy_mq.yml +++ b/.github/workflows/clippy_mq.yml @@ -15,37 +15,7 @@ defaults: shell: bash jobs: - changelog: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.ref }} - # Unsetting this would make so that any malicious package could get our Github Token - persist-credentials: false - - # Run - - name: Check Changelog - run: | - MESSAGE=$(git log --format=%B -n 1) - PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//') - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || { - echo "ERROR: PR body must contain 'changelog: ...'" - exit 1 - } - if [[ "$output" = "none" ]]; then - echo "WARNING: changelog is 'none'" - else - echo "changelog: $output" - fi - env: - PYTHONIOENCODING: 'utf-8' base: - needs: changelog strategy: matrix: include: @@ -119,7 +89,6 @@ jobs: OS: ${{ runner.os }} metadata_collection: - needs: changelog runs-on: ubuntu-latest steps: @@ -138,7 +107,6 @@ jobs: run: cargo collect-metadata integration_build: - needs: changelog runs-on: ubuntu-latest steps: @@ -228,7 +196,7 @@ jobs: INTEGRATION: ${{ matrix.integration }} conclusion: - needs: [ changelog, base, metadata_collection, integration_build, integration ] + needs: [ base, metadata_collection, integration_build, integration ] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run From 1bdee5b6b39f526dc5697f3938acf675d8be1f43 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 10 Jan 2025 18:17:55 +0100 Subject: [PATCH 025/342] CI: rerun the changelog check on PR synchronization Rerunning the PR checks when a PR is synchronized (new commits added, or force-pushed) seems to remove the changelog checks from the UI while keeping the PR mergeable from the maintainer's interface. This PR reruns the cheap changelog check when a PR is synchronized. --- .github/workflows/clippy_changelog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clippy_changelog.yml b/.github/workflows/clippy_changelog.yml index c08abd155176..6df5a48c75ff 100644 --- a/.github/workflows/clippy_changelog.yml +++ b/.github/workflows/clippy_changelog.yml @@ -3,7 +3,7 @@ name: Clippy changelog check on: merge_group: pull_request: - types: [opened, reopened, edited] + types: [opened, reopened, synchronize, edited] concurrency: # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. From 4a69d0d4d8cb2b524f486d69d7c0a06eda68fd2b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 27 Nov 2024 18:28:23 +0100 Subject: [PATCH 026/342] New lint: manual_ok_err --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/matches/manual_ok_err.rs | 135 ++++++++++++++++++++++ clippy_lints/src/matches/mod.rs | 45 ++++++++ clippy_utils/src/ty/mod.rs | 11 ++ tests/ui/manual_ok_err.fixed | 91 +++++++++++++++ tests/ui/manual_ok_err.rs | 125 ++++++++++++++++++++ tests/ui/manual_ok_err.stderr | 95 +++++++++++++++ 8 files changed, 504 insertions(+) create mode 100644 clippy_lints/src/matches/manual_ok_err.rs create mode 100644 tests/ui/manual_ok_err.fixed create mode 100644 tests/ui/manual_ok_err.rs create mode 100644 tests/ui/manual_ok_err.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 664a7e766304..89b5e1ced795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5720,6 +5720,7 @@ Released 2018-09-13 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 7451fb909ef8..9e10ae787437 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -335,6 +335,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, crate::matches::MANUAL_FILTER_INFO, crate::matches::MANUAL_MAP_INFO, + crate::matches::MANUAL_OK_ERR_INFO, crate::matches::MANUAL_UNWRAP_OR_INFO, crate::matches::MATCH_AS_REF_INFO, crate::matches::MATCH_BOOL_INFO, diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs new file mode 100644 index 000000000000..b1a555b91d1b --- /dev/null +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -0,0 +1,135 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::Ty; +use rustc_span::symbol::Ident; + +use super::MANUAL_OK_ERR; + +pub(crate) fn check_if_let( + cx: &LateContext<'_>, + expr: &Expr<'_>, + let_pat: &Pat<'_>, + let_expr: &Expr<'_>, + if_then: &Expr<'_>, + else_expr: &Expr<'_>, +) { + if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr)) + && let Some((is_ok, ident)) = is_ok_or_err(cx, let_pat) + && is_some_ident(cx, if_then, ident, inner_expr_ty) + && is_none(cx, else_expr) + { + apply_lint(cx, expr, let_expr, is_ok); + } +} + +pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) { + if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr)) + && arms.len() == 2 + && arms.iter().all(|arm| arm.guard.is_none()) + && let Some((idx, is_ok)) = arms.iter().enumerate().find_map(|(arm_idx, arm)| { + // Check if the arm is a `Ok(x) => x` or `Err(x) => x` alternative. + // In this case, return its index and whether it uses `Ok` or `Err`. + if let Some((is_ok, ident)) = is_ok_or_err(cx, arm.pat) + && is_some_ident(cx, arm.body, ident, inner_expr_ty) + { + Some((arm_idx, is_ok)) + } else { + None + } + }) + // Accept wildcard only as the second arm + && is_variant_or_wildcard(cx, arms[1-idx].pat, idx == 0, is_ok) + // Check that the body of the non `Ok`/`Err` arm is `None` + && is_none(cx, arms[1 - idx].body) + { + apply_lint(cx, expr, scrutinee, is_ok); + } +} + +/// Check that `pat` applied to a `Result` only matches `Ok(_)`, `Err(_)`, not a subset or a +/// superset of it. If `can_be_wild` is `true`, wildcards are also accepted. In the case of +/// a non-wildcard, `must_match_err` indicates whether the `Err` or the `Ok` variant should be +/// accepted. +fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool { + match pat.kind { + PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true, + PatKind::TupleStruct(qpath, ..) => { + is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + }, + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { + is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) + }, + _ => false, + } +} + +/// Return `Some((true, IDENT))` if `pat` contains `Ok(IDENT)`, `Some((false, IDENT))` if it +/// contains `Err(IDENT)`, `None` otherwise. +fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> { + if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind + && let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind + && let res = cx.qpath_res(qpath, pat.hir_id) + && let Res::Def(DefKind::Ctor(..), id) = res + && let id @ Some(_) = cx.tcx.opt_parent(id) + { + let lang_items = cx.tcx.lang_items(); + if id == lang_items.result_ok_variant() { + return Some((true, ident)); + } else if id == lang_items.result_err_variant() { + return Some((false, ident)); + } + } + None +} + +/// Check if `expr` contains `Some(ident)`, possibly as a block +fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { + if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind + && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && cx.typeck_results().expr_ty(body_arg) == ty + && let ExprKind::Path(QPath::Resolved( + _, + Path { + segments: [segment], .. + }, + )) = body_arg.kind + { + segment.ident.name == ident.name + } else { + false + } +} + +/// Check if `expr` is `None`, possibly as a block +fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} + +/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or +/// `err`, depending on `is_ok`. +fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok: bool) { + let method = if is_ok { "ok" } else { "err" }; + let mut app = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); + span_lint_and_sugg( + cx, + MANUAL_OK_ERR, + expr.span, + format!("manual implementation of `{method}`"), + "replace with", + format!("{scrut}.{method}()"), + app, + ); +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index ac1eae07eff6..a7fdd483c16c 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -2,6 +2,7 @@ mod collapsible_match; mod infallible_destructuring_match; mod manual_filter; mod manual_map; +mod manual_ok_err; mod manual_unwrap_or; mod manual_utils; mod match_as_ref; @@ -972,6 +973,40 @@ declare_clippy_lint! { "checks for unnecessary guards in match expressions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for manual implementation of `.ok()` or `.err()` + /// on `Result` values. + /// + /// ### Why is this bad? + /// Using `.ok()` or `.err()` rather than a `match` or + /// `if let` is less complex and more readable. + /// + /// ### Example + /// ```no_run + /// # fn func() -> Result { Ok(0) } + /// let a = match func() { + /// Ok(v) => Some(v), + /// Err(_) => None, + /// }; + /// let b = if let Err(v) = func() { + /// Some(v) + /// } else { + /// None + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// # fn func() -> Result { Ok(0) } + /// let a = func().ok(); + /// let b = func().err(); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_OK_ERR, + complexity, + "find manual implementations of `.ok()` or `.err()` on `Result`" +} + pub struct Matches { msrv: Msrv, infallible_destructuring_match_linted: bool, @@ -1013,6 +1048,7 @@ impl_lint_pass!(Matches => [ MANUAL_MAP, MANUAL_FILTER, REDUNDANT_GUARDS, + MANUAL_OK_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -1091,6 +1127,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { manual_unwrap_or::check_match(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); manual_filter::check_match(cx, ex, arms, expr); + manual_ok_err::check_match(cx, expr, ex, arms); } if self.infallible_destructuring_match_linted { @@ -1134,6 +1171,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_let.if_then, else_expr, ); + manual_ok_err::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 32e7c2bbf7cb..802560a80157 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1341,3 +1341,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> _ => None, } } + +/// Check if `ty` is an `Option` and return its argument type if it is. +pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) => cx + .tcx + .is_diagnostic_item(sym::Option, adt.did()) + .then(|| args.type_at(0)), + _ => None, + } +} diff --git a/tests/ui/manual_ok_err.fixed b/tests/ui/manual_ok_err.fixed new file mode 100644 index 000000000000..e7e0464c4787 --- /dev/null +++ b/tests/ui/manual_ok_err.fixed @@ -0,0 +1,91 @@ +#![warn(clippy::manual_ok_err)] + +fn funcall() -> Result { + todo!() +} + +fn main() { + let _ = funcall().ok(); + + let _ = funcall().ok(); + + let _ = funcall().err(); + + let _ = funcall().err(); + + let _ = funcall().ok(); + + let _ = funcall().err(); + + #[allow(clippy::redundant_pattern)] + let _ = funcall().ok(); + + struct S; + + impl std::ops::Neg for S { + type Output = Result; + + fn neg(self) -> Self::Output { + funcall() + } + } + + // Suggestion should be properly parenthesized + let _ = (-S).ok(); + + no_lint(); +} + +fn no_lint() { + let _ = match funcall() { + Ok(v) if v > 3 => Some(v), + _ => None, + }; + + let _ = match funcall() { + Err(_) => None, + Ok(3) => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + _ => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + Err(_) | Ok(3) => None, + Ok(v) => Some(v), + }; + + #[expect(clippy::redundant_pattern)] + let _ = match funcall() { + _v @ _ => None, + Ok(v) => Some(v), + }; + + // Content of `Option` and matching content of `Result` do + // not have the same type. + let _: Option<&dyn std::any::Any> = match Ok::<_, ()>(&1) { + Ok(v) => Some(v), + _ => None, + }; + + let _ = match Ok::<_, ()>(&1) { + _x => None, + Ok(v) => Some(v), + }; + + let _ = match Ok::<_, std::convert::Infallible>(1) { + Ok(3) => None, + Ok(v) => Some(v), + }; +} + +const fn cf(x: Result) -> Option { + // Do not lint in const code + match x { + Ok(v) => Some(v), + Err(_) => None, + } +} diff --git a/tests/ui/manual_ok_err.rs b/tests/ui/manual_ok_err.rs new file mode 100644 index 000000000000..03ad773f47cf --- /dev/null +++ b/tests/ui/manual_ok_err.rs @@ -0,0 +1,125 @@ +#![warn(clippy::manual_ok_err)] + +fn funcall() -> Result { + todo!() +} + +fn main() { + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + Err(_) => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + _v => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Err(v) => Some(v), + Ok(_) => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Err(v) => Some(v), + _v => None, + }; + + let _ = if let Ok(v) = funcall() { + //~^ manual_ok_err + Some(v) + } else { + None + }; + + let _ = if let Err(v) = funcall() { + //~^ manual_ok_err + Some(v) + } else { + None + }; + + #[allow(clippy::redundant_pattern)] + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + _v @ _ => None, + }; + + struct S; + + impl std::ops::Neg for S { + type Output = Result; + + fn neg(self) -> Self::Output { + funcall() + } + } + + // Suggestion should be properly parenthesized + let _ = match -S { + //~^ manual_ok_err + Ok(v) => Some(v), + _ => None, + }; + + no_lint(); +} + +fn no_lint() { + let _ = match funcall() { + Ok(v) if v > 3 => Some(v), + _ => None, + }; + + let _ = match funcall() { + Err(_) => None, + Ok(3) => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + _ => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + Err(_) | Ok(3) => None, + Ok(v) => Some(v), + }; + + #[expect(clippy::redundant_pattern)] + let _ = match funcall() { + _v @ _ => None, + Ok(v) => Some(v), + }; + + // Content of `Option` and matching content of `Result` do + // not have the same type. + let _: Option<&dyn std::any::Any> = match Ok::<_, ()>(&1) { + Ok(v) => Some(v), + _ => None, + }; + + let _ = match Ok::<_, ()>(&1) { + _x => None, + Ok(v) => Some(v), + }; + + let _ = match Ok::<_, std::convert::Infallible>(1) { + Ok(3) => None, + Ok(v) => Some(v), + }; +} + +const fn cf(x: Result) -> Option { + // Do not lint in const code + match x { + Ok(v) => Some(v), + Err(_) => None, + } +} diff --git a/tests/ui/manual_ok_err.stderr b/tests/ui/manual_ok_err.stderr new file mode 100644 index 000000000000..d0d5e2c81e96 --- /dev/null +++ b/tests/ui/manual_ok_err.stderr @@ -0,0 +1,95 @@ +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:8:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | Err(_) => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + | + = note: `-D clippy::manual-ok-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_ok_err)]` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:14:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _v => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:20:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Err(v) => Some(v), +LL | | Ok(_) => None, +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:26:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Err(v) => Some(v), +LL | | _v => None, +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:32:13 + | +LL | let _ = if let Ok(v) = funcall() { + | _____________^ +LL | | +LL | | Some(v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:39:13 + | +LL | let _ = if let Err(v) = funcall() { + | _____________^ +LL | | +LL | | Some(v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:47:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _v @ _ => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:64:13 + | +LL | let _ = match -S { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _ => None, +LL | | }; + | |_____^ help: replace with: `(-S).ok()` + +error: aborting due to 8 previous errors + From f5864d71376d69371fe6cb1b97c6120e05fc2ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 7 Jan 2025 15:37:59 +0000 Subject: [PATCH 027/342] migrate `clippy` to the `DenseBitSet` name --- .../src/needless_borrows_for_generic_args.rs | 4 ++-- clippy_utils/src/mir/mod.rs | 4 ++-- clippy_utils/src/mir/possible_borrower.rs | 18 +++++++++--------- clippy_utils/src/mir/possible_origin.rs | 4 ++-- clippy_utils/src/mir/transitive_relation.rs | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index f69913ddbfd9..7f91e555054d 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -9,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; @@ -390,7 +390,7 @@ fn replace_types<'tcx>( projection_predicates: &[ProjectionPredicate<'tcx>], args: &mut [GenericArg<'tcx>], ) -> bool { - let mut replaced = BitSet::new_empty(args.len()); + let mut replaced = DenseBitSet::new_empty(args.len()); let mut deque = VecDeque::with_capacity(args.len()); deque.push_back((param_ty, new_ty)); diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 3924e384c371..ccbbccd0dbff 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, HirId}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal, @@ -88,7 +88,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> { /// Checks if the block is part of a cycle pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { - let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); seen.insert(block); diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index cf73bae2583b..5eb9b3b8f227 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -2,7 +2,7 @@ use super::possible_origin::PossibleOriginVisitor; use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{self, Mutability}; @@ -21,14 +21,14 @@ struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, + possible_origin: FxHashMap>, } impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { fn new( cx: &'a LateContext<'tcx>, body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap>, + possible_origin: FxHashMap>, ) -> Self { Self { possible_borrower: TransitiveRelation::default(), @@ -56,7 +56,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { } } - let bs = BitSet::new_empty(self.body.local_decls.len()); + let bs = DenseBitSet::new_empty(self.body.local_decls.len()); PossibleBorrowerMap { map, maybe_live, @@ -119,7 +119,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> { let mut mutable_variables: Vec = mutable_borrowers .iter() .filter_map(|r| self.possible_origin.get(r)) - .flat_map(BitSet::iter) + .flat_map(DenseBitSet::iter) .collect(); if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { @@ -171,10 +171,10 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap>, + pub map: FxHashMap>, maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet, BitSet), + // Caches to avoid allocation of `DenseBitSet` on every query + pub bitset: (DenseBitSet, DenseBitSet), } impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { @@ -184,7 +184,7 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) .into_results_cursor(mir); let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs index 47b93aad20c8..3d253fd2bb14 100644 --- a/clippy_utils/src/mir/possible_origin.rs +++ b/clippy_utils/src/mir/possible_origin.rs @@ -1,7 +1,7 @@ use super::transitive_relation::TransitiveRelation; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_lint::LateContext; use rustc_middle::mir; @@ -22,7 +22,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { } } - pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { let mut map = FxHashMap::default(); for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { if is_copy(cx, self.body.local_decls[row].ty) { diff --git a/clippy_utils/src/mir/transitive_relation.rs b/clippy_utils/src/mir/transitive_relation.rs index 74d1f60af71c..da44829a4c80 100644 --- a/clippy_utils/src/mir/transitive_relation.rs +++ b/clippy_utils/src/mir/transitive_relation.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir; #[derive(Default)] @@ -12,8 +12,8 @@ impl TransitiveRelation { self.relations.entry(a).or_default().push(b); } - pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> BitSet { - let mut seen = BitSet::new_empty(domain_size); + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> DenseBitSet { + let mut seen = DenseBitSet::new_empty(domain_size); let mut stack = vec![a]; while let Some(u) = stack.pop() { if let Some(edges) = self.relations.get(&u) { From 0b402baf152ab18774716ea0c8e07a34ad83a252 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Jan 2025 12:30:17 +0100 Subject: [PATCH 028/342] Do not look for significant drop inside `.await` expansion Temporaries created inside the expansion of `.await` will be dropped and need no checking. Looking inside the expansion will trigger false positives. --- .../matches/significant_drop_in_scrutinee.rs | 5 +++-- tests/ui/significant_drop_in_scrutinee.rs | 18 ++++++++++++++++++ tests/ui/significant_drop_in_scrutinee.stderr | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 2ce6a8a85a5e..9d8e0a694339 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -441,8 +441,9 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> { let parent_expr_before = self.parent_expr.replace(ex); match ex.kind { - // Skip blocks because values in blocks will be dropped as usual. - ExprKind::Block(..) => (), + // Skip blocks because values in blocks will be dropped as usual, and await + // desugaring because temporary insides the future will have been dropped. + ExprKind::Block(..) | ExprKind::Match(_, _, MatchSource::AwaitDesugar) => (), _ => walk_expr(self, ex), } diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 8468d1d7c7d4..e9ebd23beac3 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -832,4 +832,22 @@ fn should_trigger_lint_in_while_let() { } } +async fn foo_async(mutex: &Mutex) -> Option> { + Some(mutex.lock().unwrap()) +} + +async fn should_trigger_lint_for_async(mutex: Mutex) -> i32 { + match *foo_async(&mutex).await.unwrap() { + n if n < 10 => n, + _ => 10, + } +} + +async fn should_not_trigger_lint_in_async_expansion(mutex: Mutex) -> i32 { + match foo_async(&mutex).await { + Some(guard) => *guard, + _ => 0, + } +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 62030cbe70e7..23e38948ec59 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -568,5 +568,21 @@ LL | } | = note: this might lead to deadlocks or other unexpected behavior -error: aborting due to 29 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:840:11 + | +LL | match *foo_async(&mutex).await.unwrap() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = *foo_async(&mutex).await.unwrap(); +LL ~ match value { + | + +error: aborting due to 30 previous errors From 5f757153981b3e6cc8f36d53473906c7b6b7698b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 6 Jan 2025 17:20:02 +0100 Subject: [PATCH 029/342] Do not trigger `redundant_pub_crate` in external macros --- clippy_lints/src/redundant_pub_crate.rs | 3 +++ tests/ui/redundant_pub_crate.fixed | 7 ++++++ tests/ui/redundant_pub_crate.rs | 7 ++++++ tests/ui/redundant_pub_crate.stderr | 32 ++++++++++++------------- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 1b557730ecad..8d6b1c7274d9 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; @@ -49,6 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && is_not_macro_export(item) + && !in_external_macro(cx.sess(), item.span) { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); diff --git a/tests/ui/redundant_pub_crate.fixed b/tests/ui/redundant_pub_crate.fixed index e1d845721a9c..8882a4d50a5f 100644 --- a/tests/ui/redundant_pub_crate.fixed +++ b/tests/ui/redundant_pub_crate.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![allow(dead_code)] #![warn(clippy::redundant_pub_crate)] @@ -113,4 +114,10 @@ mod issue_8732 { pub(crate) use some_macro; // ok: macro exports are exempt } +proc_macros::external! { + mod priv_mod { + pub(crate) fn dummy() {} + } +} + fn main() {} diff --git a/tests/ui/redundant_pub_crate.rs b/tests/ui/redundant_pub_crate.rs index 4d7f44892d0c..5c8cab9be161 100644 --- a/tests/ui/redundant_pub_crate.rs +++ b/tests/ui/redundant_pub_crate.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![allow(dead_code)] #![warn(clippy::redundant_pub_crate)] @@ -113,4 +114,10 @@ mod issue_8732 { pub(crate) use some_macro; // ok: macro exports are exempt } +proc_macros::external! { + mod priv_mod { + pub(crate) fn dummy() {} + } +} + fn main() {} diff --git a/tests/ui/redundant_pub_crate.stderr b/tests/ui/redundant_pub_crate.stderr index 8f1005ab9b73..699e19b1abcf 100644 --- a/tests/ui/redundant_pub_crate.stderr +++ b/tests/ui/redundant_pub_crate.stderr @@ -1,5 +1,5 @@ error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:6:5 + --> tests/ui/redundant_pub_crate.rs:7:5 | LL | pub(crate) fn g() {} // private due to m1 | ----------^^^^^ @@ -10,7 +10,7 @@ LL | pub(crate) fn g() {} // private due to m1 = help: to override `-D warnings` add `#[allow(clippy::redundant_pub_crate)]` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:11:9 + --> tests/ui/redundant_pub_crate.rs:12:9 | LL | pub(crate) fn g() {} // private due to m1_1 and m1 | ----------^^^^^ @@ -18,7 +18,7 @@ LL | pub(crate) fn g() {} // private due to m1_1 and m1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:15:5 + --> tests/ui/redundant_pub_crate.rs:16:5 | LL | pub(crate) mod m1_2 { | ----------^^^^^^^^^ @@ -26,7 +26,7 @@ LL | pub(crate) mod m1_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:18:9 + --> tests/ui/redundant_pub_crate.rs:19:9 | LL | pub(crate) fn g() {} // private due to m1_2 and m1 | ----------^^^^^ @@ -34,7 +34,7 @@ LL | pub(crate) fn g() {} // private due to m1_2 and m1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:24:9 + --> tests/ui/redundant_pub_crate.rs:25:9 | LL | pub(crate) fn g() {} // private due to m1 | ----------^^^^^ @@ -42,7 +42,7 @@ LL | pub(crate) fn g() {} // private due to m1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:31:5 + --> tests/ui/redundant_pub_crate.rs:32:5 | LL | pub(crate) fn g() {} // already crate visible due to m2 | ----------^^^^^ @@ -50,7 +50,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:36:9 + --> tests/ui/redundant_pub_crate.rs:37:9 | LL | pub(crate) fn g() {} // private due to m2_1 | ----------^^^^^ @@ -58,7 +58,7 @@ LL | pub(crate) fn g() {} // private due to m2_1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:40:5 + --> tests/ui/redundant_pub_crate.rs:41:5 | LL | pub(crate) mod m2_2 { | ----------^^^^^^^^^ @@ -66,7 +66,7 @@ LL | pub(crate) mod m2_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:43:9 + --> tests/ui/redundant_pub_crate.rs:44:9 | LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 | ----------^^^^^ @@ -74,7 +74,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:49:9 + --> tests/ui/redundant_pub_crate.rs:50:9 | LL | pub(crate) fn g() {} // already crate visible due to m2 | ----------^^^^^ @@ -82,7 +82,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:61:9 + --> tests/ui/redundant_pub_crate.rs:62:9 | LL | pub(crate) fn g() {} // private due to m3_1 | ----------^^^^^ @@ -90,7 +90,7 @@ LL | pub(crate) fn g() {} // private due to m3_1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:68:9 + --> tests/ui/redundant_pub_crate.rs:69:9 | LL | pub(crate) fn g() {} // already crate visible due to m3_2 | ----------^^^^^ @@ -98,7 +98,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m3_2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:81:5 + --> tests/ui/redundant_pub_crate.rs:82:5 | LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` | ----------^^^^^ @@ -106,7 +106,7 @@ LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:86:9 + --> tests/ui/redundant_pub_crate.rs:87:9 | LL | pub(crate) fn g() {} // private due to m4_1 | ----------^^^^^ @@ -114,7 +114,7 @@ LL | pub(crate) fn g() {} // private due to m4_1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:90:5 + --> tests/ui/redundant_pub_crate.rs:91:5 | LL | pub(crate) mod m4_2 { | ----------^^^^^^^^^ @@ -122,7 +122,7 @@ LL | pub(crate) mod m4_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:93:9 + --> tests/ui/redundant_pub_crate.rs:94:9 | LL | pub(crate) fn g() {} // private due to m4_2 | ----------^^^^^ From a73166872dbab504775ed26a361bc6eb60c2657b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 13 Jan 2025 13:59:47 +0100 Subject: [PATCH 030/342] In edition 2024, `std::env::set_var` is `unsafe` --- tests/compile-test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index e2e4d92df79f..d1b1a1d23232 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -300,7 +300,9 @@ fn run_ui_cargo(cx: &TestContext) { } fn main() { - set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + unsafe { + set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + } let cx = TestContext::new(); From 0456e4d6a16b1d9148dad5e5bf41e1d8f4ccca2d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 13 Jan 2025 15:54:02 +0100 Subject: [PATCH 031/342] In edition 2024, `gen` is a reserved keyword --- tests/ui/crashes/ice-11422.fixed | 2 +- tests/ui/crashes/ice-11422.rs | 2 +- tests/ui/crashes/ice-11422.stderr | 10 +++++----- tests/ui/implicit_hasher.fixed | 2 +- tests/ui/implicit_hasher.rs | 2 +- tests/ui/implicit_hasher.stderr | 2 +- tests/ui/map_with_unused_argument_over_ranges.fixed | 4 ++-- tests/ui/map_with_unused_argument_over_ranges.rs | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/ui/crashes/ice-11422.fixed b/tests/ui/crashes/ice-11422.fixed index ca5721cbb2ba..d996b1db08a6 100644 --- a/tests/ui/crashes/ice-11422.fixed +++ b/tests/ui/crashes/ice-11422.fixed @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::ops::*; -fn gen() -> impl PartialOrd + Debug {} +fn r#gen() -> impl PartialOrd + Debug {} struct Bar {} trait Foo {} diff --git a/tests/ui/crashes/ice-11422.rs b/tests/ui/crashes/ice-11422.rs index 355ec2480bba..eb89b7c38f43 100644 --- a/tests/ui/crashes/ice-11422.rs +++ b/tests/ui/crashes/ice-11422.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::ops::*; -fn gen() -> impl PartialOrd + PartialEq + Debug {} +fn r#gen() -> impl PartialOrd + PartialEq + Debug {} struct Bar {} trait Foo {} diff --git a/tests/ui/crashes/ice-11422.stderr b/tests/ui/crashes/ice-11422.stderr index a340977f4699..67944e4e6e80 100644 --- a/tests/ui/crashes/ice-11422.stderr +++ b/tests/ui/crashes/ice-11422.stderr @@ -1,15 +1,15 @@ error: this bound is already specified as the supertrait of `PartialOrd` - --> tests/ui/crashes/ice-11422.rs:6:31 + --> tests/ui/crashes/ice-11422.rs:6:33 | -LL | fn gen() -> impl PartialOrd + PartialEq + Debug {} - | ^^^^^^^^^ +LL | fn r#gen() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ | = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]` help: try removing this bound | -LL - fn gen() -> impl PartialOrd + PartialEq + Debug {} -LL + fn gen() -> impl PartialOrd + Debug {} +LL - fn r#gen() -> impl PartialOrd + PartialEq + Debug {} +LL + fn r#gen() -> impl PartialOrd + Debug {} | error: aborting due to 1 previous error diff --git a/tests/ui/implicit_hasher.fixed b/tests/ui/implicit_hasher.fixed index 2d6dc0274cf2..971746ae95d7 100644 --- a/tests/ui/implicit_hasher.fixed +++ b/tests/ui/implicit_hasher.fixed @@ -70,7 +70,7 @@ pub fn map(map: &mut HashMap) {} pub fn set(set: &mut HashSet) {} #[inline_macros] -pub mod gen { +pub mod gen_ { use super::*; inline! { impl Foo for HashMap { diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs index 0a334357bd1b..b34aa1f81374 100644 --- a/tests/ui/implicit_hasher.rs +++ b/tests/ui/implicit_hasher.rs @@ -70,7 +70,7 @@ pub fn map(map: &mut HashMap) {} pub fn set(set: &mut HashSet) {} #[inline_macros] -pub mod gen { +pub mod gen_ { use super::*; inline! { impl Foo for HashMap { diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr index 48c6ebc209cf..442f4789aacf 100644 --- a/tests/ui/implicit_hasher.stderr +++ b/tests/ui/implicit_hasher.stderr @@ -98,7 +98,7 @@ error: impl for `HashMap` should be generalized over different hashers LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ | - = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_gen_` (in Nightly builds, run with -Z macro-backtrace for more info) help: add a type parameter for `BuildHasher` | LL ~ impl Foo for HashMap { diff --git a/tests/ui/map_with_unused_argument_over_ranges.fixed b/tests/ui/map_with_unused_argument_over_ranges.fixed index cf520e71a64f..18716e93d1e9 100644 --- a/tests/ui/map_with_unused_argument_over_ranges.fixed +++ b/tests/ui/map_with_unused_argument_over_ranges.fixed @@ -14,7 +14,7 @@ fn do_something_interesting(x: usize, y: usize) -> usize { todo!() } -macro_rules! gen { +macro_rules! r#gen { () => { (0..10).map(|_| do_something()); }; @@ -45,7 +45,7 @@ fn main() { std::iter::repeat_with(|| do_something()).take(1); std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); // These should not be raised - gen!(); + r#gen!(); let lower = 2; let lower_fn = || 2; (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled diff --git a/tests/ui/map_with_unused_argument_over_ranges.rs b/tests/ui/map_with_unused_argument_over_ranges.rs index 298eee9ca3fa..596afd51e61f 100644 --- a/tests/ui/map_with_unused_argument_over_ranges.rs +++ b/tests/ui/map_with_unused_argument_over_ranges.rs @@ -14,7 +14,7 @@ fn do_something_interesting(x: usize, y: usize) -> usize { todo!() } -macro_rules! gen { +macro_rules! r#gen { () => { (0..10).map(|_| do_something()); }; @@ -45,7 +45,7 @@ fn main() { (9..=9).map(|_| do_something()); (1..=1 << 4).map(|_| do_something()); // These should not be raised - gen!(); + r#gen!(); let lower = 2; let lower_fn = || 2; (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled From 6c367cb83fe02417cc3f46c1ba7e4ddc11fc5719 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 13 Jan 2025 16:55:14 +0100 Subject: [PATCH 032/342] Update mdbook to 0.4.43 --- .github/workflows/remark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 69d00dc027e8..13902f78b541 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -27,7 +27,7 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run From a4805ff61049d746b8193ba125af525d9d6165fa Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 10 Jan 2025 17:59:04 +0100 Subject: [PATCH 033/342] Select edition 2024 --- Cargo.toml | 2 +- book/book.toml | 2 +- book/src/development/adding_lints.md | 4 ++-- clippy_config/Cargo.toml | 2 +- clippy_dev/Cargo.toml | 2 +- clippy_dummy/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- lintcheck/Cargo.toml | 2 +- rustc_tools_util/Cargo.toml | 2 +- rustfmt.toml | 4 ++-- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e9b11d5df2f2..b615bb9e6f6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] categories = ["development-tools", "development-tools::cargo-plugins"] build = "build.rs" -edition = "2021" +edition = "2024" publish = false [[bin]] diff --git a/book/book.toml b/book/book.toml index 93b6641f7e1e..c918aadf83c4 100644 --- a/book/book.toml +++ b/book/book.toml @@ -6,7 +6,7 @@ src = "src" title = "Clippy Documentation" [rust] -edition = "2018" +edition = "2024" [output.html] edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}" diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index c07568697d02..7bb2ee46cf0d 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -537,7 +537,7 @@ via `Tools -> Clippy` and you should see the generated code in the output below. If the command was executed successfully, you can copy the code over to where you are implementing your lint. -[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 ## Print HIR lint @@ -552,7 +552,7 @@ attribute to expressions you often need to enable _Clippy_. [_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html -[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=daf14db3a7f39ca467cd1b86c34b9afb ## Documentation diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index c761e207c6b6..e473a5839402 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -3,7 +3,7 @@ name = "clippy_config" # begin autogenerated version version = "0.1.86" # end autogenerated version -edition = "2021" +edition = "2024" publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index d3a103eaf4c6..47b7b3758613 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -2,7 +2,7 @@ name = "clippy_dev" description = "Clippy developer tooling" version = "0.0.1" -edition = "2021" +edition = "2024" [dependencies] aho-corasick = "1.0" diff --git a/clippy_dummy/Cargo.toml b/clippy_dummy/Cargo.toml index c206a1eb07b5..61bdd421c764 100644 --- a/clippy_dummy/Cargo.toml +++ b/clippy_dummy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_dummy" # rename to clippy before publishing version = "0.0.303" -edition = "2018" +edition = "2024" readme = "crates-readme.md" description = "A bunch of helpful lints to avoid common pitfalls in Rust." build = 'build.rs' diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index b575ac1bf4cc..c62a7ec783b6 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] -edition = "2021" +edition = "2024" [dependencies] arrayvec = { version = "0.7", default-features = false } diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 7fa070cd226b..68b7e1592e2e 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -3,7 +3,7 @@ name = "clippy_utils" # begin autogenerated version version = "0.1.86" # end autogenerated version -edition = "2021" +edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index b0e4e3e3e573..55e588f5ec73 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -6,7 +6,7 @@ readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] -edition = "2021" +edition = "2024" publish = false default-run = "lintcheck" diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index b63632916ba1..cba025639482 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -7,6 +7,6 @@ readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["rustc", "tool", "git", "version", "hash"] categories = ["development-tools"] -edition = "2018" +edition = "2024" [dependencies] diff --git a/rustfmt.toml b/rustfmt.toml index 4248f42f654b..0dc6adce7bfc 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -2,8 +2,8 @@ max_width = 120 comment_width = 100 match_block_trailing_comma = true wrap_comments = true -edition = "2021" +edition = "2024" error_on_line_overflow = true imports_granularity = "Module" -version = "Two" +style_edition = "2024" ignore = ["tests/ui/crashes/ice-10912.rs"] From dc23fa5e6c2b6b783a23ef7e6835f7867361ddaf Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 6 Jan 2025 07:48:47 +0100 Subject: [PATCH 034/342] Suggest `manual_div_ceil` even when right operand is a constant --- clippy_lints/src/manual_div_ceil.rs | 36 ++++++++++++++++++++ tests/ui/manual_div_ceil.fixed | 11 ++++++ tests/ui/manual_div_ceil.rs | 11 ++++++ tests/ui/manual_div_ceil.stderr | 14 +++++++- tests/ui/manual_div_ceil_with_feature.fixed | 11 ++++++ tests/ui/manual_div_ceil_with_feature.rs | 11 ++++++ tests/ui/manual_div_ceil_with_feature.stderr | 32 ++++++++++++++++- 7 files changed, 124 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/manual_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index aa59b047b169..816ca17b3d2d 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -102,12 +102,48 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { { build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); } + + // (x + (Y - 1)) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + } + + // ((Y - 1) + x) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) { + build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); + } + + // (x - (-Y - 1)) / Y + if inner_op.node == BinOpKind::Sub + && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind + && differ_by_one(abs_div_rhs, inner_rhs) + { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + } } } extract_msrv_attr!(LateContext); } +/// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 == +/// large_expr`. +fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(small) = small_expr.kind + && let ExprKind::Lit(large) = large_expr.kind + && let LitKind::Int(s, _) = small.node + && let LitKind::Int(l, _) = large.node + { + Some(l.get()) == s.get().checked_add(1) + } else if let ExprKind::Unary(UnOp::Neg, small_inner_expr) = small_expr.kind + && let ExprKind::Unary(UnOp::Neg, large_inner_expr) = large_expr.kind + { + differ_by_one(large_inner_expr, small_inner_expr) + } else { + false + } +} + fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.peel_refs().kind() { diff --git a/tests/ui/manual_div_ceil.fixed b/tests/ui/manual_div_ceil.fixed index 1fb1df5b4425..f6eb5a9784ac 100644 --- a/tests/ui/manual_div_ceil.fixed +++ b/tests/ui/manual_div_ceil.fixed @@ -50,3 +50,14 @@ fn issue_13843() { let _ = 1_000_000_u32.div_ceil(6u32); } + +fn issue_13950() { + let x = 33u32; + let _ = x.div_ceil(8); + let _ = x.div_ceil(8); + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/tests/ui/manual_div_ceil.rs b/tests/ui/manual_div_ceil.rs index 4f6d38f0d145..2f063afe787d 100644 --- a/tests/ui/manual_div_ceil.rs +++ b/tests/ui/manual_div_ceil.rs @@ -50,3 +50,14 @@ fn issue_13843() { let _ = (1_000_000 + 6u32 - 1) / 6u32; } + +fn issue_13950() { + let x = 33u32; + let _ = (x + 7) / 8; + let _ = (7 + x) / 8; + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/tests/ui/manual_div_ceil.stderr b/tests/ui/manual_div_ceil.stderr index 3d87fe8e0409..0bac5d8ef1c1 100644 --- a/tests/ui/manual_div_ceil.stderr +++ b/tests/ui/manual_div_ceil.stderr @@ -85,5 +85,17 @@ error: manually reimplementing `div_ceil` LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` -error: aborting due to 14 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:57:13 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: aborting due to 16 previous errors diff --git a/tests/ui/manual_div_ceil_with_feature.fixed b/tests/ui/manual_div_ceil_with_feature.fixed index f32b78aa14d0..01c58151bc94 100644 --- a/tests/ui/manual_div_ceil_with_feature.fixed +++ b/tests/ui/manual_div_ceil_with_feature.fixed @@ -50,3 +50,14 @@ fn issue_13843() { let _ = 1_000_000_u32.div_ceil(6u32); } + +fn issue_13950() { + let x = 33u32; + let _ = x.div_ceil(8); + let _ = x.div_ceil(8); + + let y = -33i32; + let _ = y.div_ceil(-7); + let _ = y.div_ceil(-7); + let _ = y.div_ceil(-7); +} diff --git a/tests/ui/manual_div_ceil_with_feature.rs b/tests/ui/manual_div_ceil_with_feature.rs index 54d89fcbd462..048ff401581f 100644 --- a/tests/ui/manual_div_ceil_with_feature.rs +++ b/tests/ui/manual_div_ceil_with_feature.rs @@ -50,3 +50,14 @@ fn issue_13843() { let _ = (1_000_000 + 6u32 - 1) / 6u32; } + +fn issue_13950() { + let x = 33u32; + let _ = (x + 7) / 8; + let _ = (7 + x) / 8; + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/tests/ui/manual_div_ceil_with_feature.stderr b/tests/ui/manual_div_ceil_with_feature.stderr index c5e8c1a687cd..807cfd82724e 100644 --- a/tests/ui/manual_div_ceil_with_feature.stderr +++ b/tests/ui/manual_div_ceil_with_feature.stderr @@ -109,5 +109,35 @@ error: manually reimplementing `div_ceil` LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` -error: aborting due to 18 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:56:13 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:57:13 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:60:13 + | +LL | let _ = (y + -8) / -7; + | ^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:61:13 + | +LL | let _ = (-8 + y) / -7; + | ^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:62:13 + | +LL | let _ = (y - 8) / -7; + | ^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: aborting due to 23 previous errors From 8b7cfc75dd1925f2f5b01a6bf1291c778d2fd224 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 13 Jan 2025 10:02:46 +0100 Subject: [PATCH 035/342] Rust 1.81 and later support elision with explicit self types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9ef6e2199c885ffd671b321dfbf16ff0934f4d80 introduced a check to ensure that Clippy doesn't consider a lifetime present in an explicit self type as being the default for an elided output lifetime. For example, elision did not work in the case like: ```rust fn func(self: &Rc, &str) -> &str { … } ``` Since Rust 1.81.0, the lifetime in the self type is now considered the default for elision. Elision should then be suggested when appropriate. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/lifetimes.rs | 52 +++++++--- clippy_utils/src/msrvs.rs | 2 +- tests/ui/needless_lifetimes.fixed | 28 +++++- tests/ui/needless_lifetimes.rs | 20 +++- tests/ui/needless_lifetimes.stderr | 154 +++++++++++++++++++---------- 6 files changed, 184 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1e8cfe22b229..a0eae8f6d1cb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -681,7 +681,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); + store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf))); store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv)); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 0d71be61651d..540d8b26f8e5 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,4 +1,6 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::trait_ref_of_method; use itertools::Itertools; use rustc_ast::visit::{try_visit, walk_list}; @@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{Ident, kw}; @@ -91,7 +93,19 @@ declare_clippy_lint! { "unused lifetimes in function definitions" } -declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); +pub struct Lifetimes { + msrv: Msrv, +} + +impl Lifetimes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { .. } = item.kind { - check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { item.generics, item.span, report_extra_lifetimes, + &self.msrv, ); } } @@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv); } } + + extract_msrv_attr!(LateContext); } +#[allow(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>( generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, + msrv: &Msrv, ) { if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; @@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>( } } - if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) { if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { return; } @@ -216,6 +235,7 @@ fn could_use_elision<'tcx>( body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], + msrv: &Msrv, ) -> Option<(Vec, Vec)> { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -249,17 +269,17 @@ fn could_use_elision<'tcx>( let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; - if let Some(trait_sig) = trait_sig { - if explicit_self_type(cx, func, trait_sig.first().copied()) { - return None; - } + if let Some(trait_sig) = trait_sig + && non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv) + { + return None; } if let Some(body_id) = body { let body = cx.tcx.hir().body(body_id); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); - if explicit_self_type(cx, func, first_ident) { + if non_elidable_self_type(cx, func, first_ident, msrv) { return None; } @@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { - if let Some(ident) = ident +// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064 +fn non_elidable_self_type<'tcx>( + cx: &LateContext<'tcx>, + func: &FnDecl<'tcx>, + ident: Option, + msrv: &Msrv, +) -> bool { + if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION) + && let Some(ident) = ident && ident.name == kw::SelfLower && !func.implicit_self.has_implicit_self() && let Some(self_ty) = func.inputs.first() diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 2169a5fdd63b..62b70ef6af4d 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -20,7 +20,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } - 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE } + 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } 1,80,0 { BOX_INTO_ITER } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index 77188254316a..86cf9a9cdb63 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -6,6 +6,7 @@ clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, + clippy::redundant_allocation, clippy::unnecessary_wraps, dyn_drop, clippy::get_first @@ -443,11 +444,20 @@ mod issue7296 { fn implicit_mut(&mut self) -> &() { &() } - - fn explicit<'a>(self: &'a Arc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit(self: &Arc) -> &() { &() } - fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit_mut(self: &mut Rc) -> &() { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a () { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_mut_older<'a>(self: &'a mut Rc) -> &'a () { &() } @@ -462,8 +472,16 @@ mod issue7296 { &() } - fn explicit<'a>(self: &'a Arc) -> &'a (); - fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit(self: &Arc) -> &(); + #[clippy::msrv = "1.81"] + fn explicit_provided(self: &Arc) -> &() { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.80"] + fn explicit_provided_older<'a>(self: &'a Arc) -> &'a () { &() } diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index c74121d0d7b8..1ee0f4c6092a 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -6,6 +6,7 @@ clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, + clippy::redundant_allocation, clippy::unnecessary_wraps, dyn_drop, clippy::get_first @@ -443,13 +444,22 @@ mod issue7296 { fn implicit_mut<'a>(&'a mut self) -> &'a () { &() } - + #[clippy::msrv = "1.81"] fn explicit<'a>(self: &'a Arc) -> &'a () { &() } + #[clippy::msrv = "1.81"] fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { &() } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a () { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_mut_older<'a>(self: &'a mut Rc) -> &'a () { + &() + } fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { &() @@ -462,10 +472,18 @@ mod issue7296 { &() } + #[clippy::msrv = "1.81"] fn explicit<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.81"] fn explicit_provided<'a>(self: &'a Arc) -> &'a () { &() } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.80"] + fn explicit_provided_older<'a>(self: &'a Arc) -> &'a () { + &() + } fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 5a739201d3d0..465d529bf16c 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: elided lifetime has a name - --> tests/ui/needless_lifetimes.rs:266:52 + --> tests/ui/needless_lifetimes.rs:267:52 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | -- ^ this elided lifetime gets resolved as `'a` @@ -10,7 +10,7 @@ LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:17:23 + --> tests/ui/needless_lifetimes.rs:18:23 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^ ^^ ^^ ^^ @@ -24,7 +24,7 @@ LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:19:24 + --> tests/ui/needless_lifetimes.rs:20:24 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^ ^^ ^^ ^^ @@ -36,7 +36,7 @@ LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:29:15 + --> tests/ui/needless_lifetimes.rs:30:15 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^ ^^ ^^ @@ -48,7 +48,7 @@ LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:41:31 + --> tests/ui/needless_lifetimes.rs:42:31 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^ ^^ @@ -60,7 +60,7 @@ LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:48:27 + --> tests/ui/needless_lifetimes.rs:49:27 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^ ^^ @@ -72,7 +72,7 @@ LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:65:26 + --> tests/ui/needless_lifetimes.rs:66:26 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^ ^^ @@ -84,7 +84,7 @@ LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:72:22 + --> tests/ui/needless_lifetimes.rs:73:22 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^ ^^ @@ -96,7 +96,7 @@ LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:81:21 + --> tests/ui/needless_lifetimes.rs:82:21 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^ ^^ ^^ @@ -108,7 +108,7 @@ LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:86:28 + --> tests/ui/needless_lifetimes.rs:87:28 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^ ^^ ^^ @@ -120,7 +120,7 @@ LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:98:21 + --> tests/ui/needless_lifetimes.rs:99:21 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^ ^^ ^^ ^^ @@ -132,7 +132,7 @@ LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:122:15 + --> tests/ui/needless_lifetimes.rs:123:15 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^ ^^ ^^ @@ -144,7 +144,7 @@ LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:152:21 + --> tests/ui/needless_lifetimes.rs:153:21 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^ ^^ ^^ @@ -156,7 +156,7 @@ LL + fn self_and_out(&self) -> &u8 { | error: the following explicit lifetimes could be elided: 't - --> tests/ui/needless_lifetimes.rs:159:30 + --> tests/ui/needless_lifetimes.rs:160:30 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^ ^^ @@ -168,7 +168,7 @@ LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:166:26 + --> tests/ui/needless_lifetimes.rs:167:26 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^ ^^ @@ -180,7 +180,7 @@ LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { | error: the following explicit lifetimes could be elided: 's, 't - --> tests/ui/needless_lifetimes.rs:170:29 + --> tests/ui/needless_lifetimes.rs:171:29 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^ ^^ ^^ ^^ @@ -192,7 +192,7 @@ LL + fn distinct_self_and_in(&self, _x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:189:19 + --> tests/ui/needless_lifetimes.rs:190:19 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^ ^^ ^^ @@ -204,7 +204,7 @@ LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:207:25 + --> tests/ui/needless_lifetimes.rs:208:25 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^ ^^ @@ -216,7 +216,7 @@ LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:215:21 + --> tests/ui/needless_lifetimes.rs:216:21 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^ ^^ @@ -228,7 +228,7 @@ LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:230:22 + --> tests/ui/needless_lifetimes.rs:231:22 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^ ^^ ^^ @@ -240,7 +240,7 @@ LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:236:18 + --> tests/ui/needless_lifetimes.rs:237:18 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^ ^^ ^^ @@ -252,7 +252,7 @@ LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:254:24 + --> tests/ui/needless_lifetimes.rs:255:24 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^ ^^ @@ -264,7 +264,7 @@ LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:262:20 + --> tests/ui/needless_lifetimes.rs:263:20 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^ ^^ @@ -276,7 +276,7 @@ LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:266:30 + --> tests/ui/needless_lifetimes.rs:267:30 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^ ^^ ^ @@ -288,7 +288,7 @@ LL + fn named_input_elided_output(_arg: &str) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:274:19 + --> tests/ui/needless_lifetimes.rs:275:19 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^ ^^ @@ -300,7 +300,7 @@ LL + fn trait_bound_ok>(_: &u8, _: T) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:310:24 + --> tests/ui/needless_lifetimes.rs:311:24 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^ ^^ ^^ @@ -312,7 +312,7 @@ LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:317:24 + --> tests/ui/needless_lifetimes.rs:318:24 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^ ^^ @@ -324,7 +324,7 @@ LL + fn needless_lt(x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:321:24 + --> tests/ui/needless_lifetimes.rs:322:24 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^ ^^ @@ -336,7 +336,7 @@ LL + fn needless_lt(_x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:332:10 + --> tests/ui/needless_lifetimes.rs:333:10 | LL | impl<'a> Foo for Baz<'a> {} | ^^ ^^ @@ -348,7 +348,7 @@ LL + impl Foo for Baz<'_> {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:334:16 + --> tests/ui/needless_lifetimes.rs:335:16 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^ ^^ ^^ @@ -360,7 +360,7 @@ LL + fn baz(&self) -> impl Foo + '_ { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:366:55 + --> tests/ui/needless_lifetimes.rs:367:55 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -372,7 +372,7 @@ LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(& | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:375:26 + --> tests/ui/needless_lifetimes.rs:376:26 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^ ^^ ^^ @@ -384,7 +384,7 @@ LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:387:30 + --> tests/ui/needless_lifetimes.rs:388:30 | LL | fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^ ^^ ^^ @@ -396,7 +396,7 @@ LL + fn where_clause_elidable(i: &i32, f: T) -> &i32 | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:402:28 + --> tests/ui/needless_lifetimes.rs:403:28 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -408,7 +408,7 @@ LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:415:28 + --> tests/ui/needless_lifetimes.rs:416:28 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^ ^^ @@ -420,7 +420,7 @@ LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:418:28 + --> tests/ui/needless_lifetimes.rs:419:28 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^ ^^ @@ -432,7 +432,7 @@ LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:440:21 + --> tests/ui/needless_lifetimes.rs:441:21 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -444,7 +444,7 @@ LL + fn implicit(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:443:25 + --> tests/ui/needless_lifetimes.rs:444:25 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^ ^^ ^^ @@ -456,7 +456,31 @@ LL + fn implicit_mut(&mut self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:454:31 + --> tests/ui/needless_lifetimes.rs:448:21 + | +LL | fn explicit<'a>(self: &'a Arc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit<'a>(self: &'a Arc) -> &'a () { +LL + fn explicit(self: &Arc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:452:25 + | +LL | fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { +LL + fn explicit_mut(self: &mut Rc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:464:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -468,7 +492,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:460:21 + --> tests/ui/needless_lifetimes.rs:470:21 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^ ^^ ^^ @@ -480,7 +504,7 @@ LL + fn implicit(&self) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:461:30 + --> tests/ui/needless_lifetimes.rs:471:30 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -492,7 +516,31 @@ LL + fn implicit_provided(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:470:31 + --> tests/ui/needless_lifetimes.rs:476:21 + | +LL | fn explicit<'a>(self: &'a Arc) -> &'a (); + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit<'a>(self: &'a Arc) -> &'a (); +LL + fn explicit(self: &Arc) -> &(); + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:478:30 + | +LL | fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit_provided<'a>(self: &'a Arc) -> &'a () { +LL + fn explicit_provided(self: &Arc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:488:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^ ^^ ^^ @@ -504,7 +552,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:471:40 + --> tests/ui/needless_lifetimes.rs:489:40 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -516,7 +564,7 @@ LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:480:12 + --> tests/ui/needless_lifetimes.rs:498:12 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^ ^^ @@ -528,7 +576,7 @@ LL + fn foo(x: &u8, y: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:482:12 + --> tests/ui/needless_lifetimes.rs:500:12 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^ ^^ @@ -540,7 +588,7 @@ LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:489:18 + --> tests/ui/needless_lifetimes.rs:507:18 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -552,7 +600,7 @@ LL + fn one_input(x: &u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:494:42 + --> tests/ui/needless_lifetimes.rs:512:42 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^ ^^ @@ -564,7 +612,7 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:510:22 + --> tests/ui/needless_lifetimes.rs:528:22 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -577,7 +625,7 @@ LL + fn one_input(x: &u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'py - --> tests/ui/needless_lifetimes.rs:605:14 + --> tests/ui/needless_lifetimes.rs:623:14 | LL | impl<'t, 'py> ContentString<'t> { | ^^^ @@ -593,7 +641,7 @@ LL ~ fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Conte | error: the following explicit lifetimes could be elided: 'py - --> tests/ui/needless_lifetimes.rs:615:14 + --> tests/ui/needless_lifetimes.rs:633:14 | LL | impl<'t, 'py> ContentString<'t> { | ^^^ @@ -609,7 +657,7 @@ LL ~ fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Co | error: the following explicit lifetimes could be elided: 'py - --> tests/ui/needless_lifetimes.rs:635:14 + --> tests/ui/needless_lifetimes.rs:653:14 | LL | impl<'t, 'py> ContentString<'t> { | ^^^ @@ -627,7 +675,7 @@ LL ~ ) -> Content<'t, '_> { | error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_lifetimes.rs:656:9 + --> tests/ui/needless_lifetimes.rs:674:9 | LL | &x.b | ^^^^ help: change this to: `x.b` @@ -635,5 +683,5 @@ LL | &x.b = note: `-D clippy::needless-borrow` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` -error: aborting due to 52 previous errors +error: aborting due to 56 previous errors From 35dbaf8a618403e4a9ae2da04eb479d6450c7c88 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 13 Jan 2025 11:26:22 +0100 Subject: [PATCH 036/342] New lint `useless-nonzero-new_unchecked` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 30 ++++++++++ .../methods/useless_nonzero_new_unchecked.rs | 59 +++++++++++++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/useless_nonzero_new_unchecked.fixed | 52 ++++++++++++++++ tests/ui/useless_nonzero_new_unchecked.rs | 52 ++++++++++++++++ tests/ui/useless_nonzero_new_unchecked.stderr | 37 ++++++++++++ 8 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/useless_nonzero_new_unchecked.rs create mode 100644 tests/ui/useless_nonzero_new_unchecked.fixed create mode 100644 tests/ui/useless_nonzero_new_unchecked.rs create mode 100644 tests/ui/useless_nonzero_new_unchecked.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c71281acd48..ceb08eeac3ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6218,6 +6218,7 @@ Released 2018-09-13 [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq +[`useless_nonzero_new_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_nonzero_new_unchecked [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ac933d7cd705..5e6cfd94283c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -496,6 +496,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, + crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO, crate::methods::VEC_RESIZE_TO_ZERO_INFO, crate::methods::VERBOSE_FILE_READS_INFO, crate::methods::WAKER_CLONE_WAKE_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f66466896e5b..d7e9f65bfa3a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -130,6 +130,7 @@ mod unnecessary_to_owned; mod unused_enumerate_index; mod unwrap_expect_used; mod useless_asref; +mod useless_nonzero_new_unchecked; mod utils; mod vec_resize_to_zero; mod verbose_file_reads; @@ -4311,6 +4312,33 @@ declare_clippy_lint! { "using `Iterator::last` on a `DoubleEndedIterator`" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `NonZero*::new_unchecked()` being used in a `const` context. + /// + /// ### Why is this bad? + /// + /// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a + /// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical + /// runtime performances while not requiring `unsafe`. + /// + /// ### Example + /// ```no_run + /// use std::num::NonZeroUsize; + /// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + /// ``` + /// Use instead: + /// ```no_run + /// use std::num::NonZeroUsize; + /// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap(); + /// ``` + #[clippy::version = "1.86.0"] + pub USELESS_NONZERO_NEW_UNCHECKED, + complexity, + "using `NonZero::new_unchecked()` in a `const` context" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4477,6 +4505,7 @@ impl_lint_pass!(Methods => [ MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, DOUBLE_ENDED_ITERATOR_LAST, + USELESS_NONZERO_NEW_UNCHECKED, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4505,6 +4534,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { from_iter_instead_of_collect::check(cx, expr, args, func); unnecessary_fallible_conversions::check_function(cx, expr, func); manual_c_str_literals::check(cx, expr, func, args, &self.msrv); + useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv); }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; diff --git a/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs new file mode 100644 index 000000000000..0bd50429c09d --- /dev/null +++ b/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -0,0 +1,59 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::is_inside_always_const_context; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::USELESS_NONZERO_NEW_UNCHECKED; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) { + if msrv.meets(msrvs::CONST_UNWRAP) + && let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind + && segment.ident.name == sym::new_unchecked + && let [init_arg] = args + && is_inside_always_const_context(cx.tcx, expr.hir_id) + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + { + let mut app = Applicability::MachineApplicable; + let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app); + let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context"); + let sugg = format!( + "{ty_str}::new({}).unwrap()", + snippet_with_applicability(cx, init_arg.span, "_", &mut app) + ); + + if let Node::Block(Block { + stmts: [], + span: block_span, + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }) = cx.tcx.parent_hir_node(expr.hir_id) + { + if !block_span.from_expansion() { + // The expression is the only component of an `unsafe` block. Propose + // to replace the block altogether. + span_lint_and_sugg( + cx, + USELESS_NONZERO_NEW_UNCHECKED, + *block_span, + msg, + "use instead", + sugg, + app, + ); + } + } else { + // The expression is enclosed in a larger `unsafe` context. Indicate that + // this may no longer be needed for the fixed expression. + span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| { + diagnostic + .span_suggestion(expr.span, "use instead", sugg, app) + .note("the fixed expression does not require an `unsafe` context"); + }); + } + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 2169a5fdd63b..8f78889707ad 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -18,7 +18,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } + 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE } 1,80,0 { BOX_INTO_ITER } diff --git a/tests/ui/useless_nonzero_new_unchecked.fixed b/tests/ui/useless_nonzero_new_unchecked.fixed new file mode 100644 index 000000000000..03b34afa54ec --- /dev/null +++ b/tests/ui/useless_nonzero_new_unchecked.fixed @@ -0,0 +1,52 @@ +#![warn(clippy::useless_nonzero_new_unchecked)] + +use std::num::{NonZero, NonZeroUsize}; + +#[clippy::msrv = "1.83"] +const fn func() -> NonZeroUsize { + const { NonZeroUsize::new(3).unwrap() } + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context +} + +#[clippy::msrv = "1.82"] +const fn func_older() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_performance_hit_if_linted() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(x) } +} + +macro_rules! uns { + ($expr:expr) => { + unsafe { $expr } + }; +} + +macro_rules! nzu { + () => { + NonZeroUsize::new_unchecked(1) + }; +} + +fn main() { + const _A: NonZeroUsize = NonZeroUsize::new(3).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + static _B: NonZero = NonZero::::new(42).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _C: usize = unsafe { NonZeroUsize::new(3).unwrap().get() }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const AUX: usize = 3; + const _D: NonZeroUsize = NonZeroUsize::new(AUX).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3)); + const _Y: NonZeroUsize = unsafe { nzu!() }; +} diff --git a/tests/ui/useless_nonzero_new_unchecked.rs b/tests/ui/useless_nonzero_new_unchecked.rs new file mode 100644 index 000000000000..d450e3a03ec5 --- /dev/null +++ b/tests/ui/useless_nonzero_new_unchecked.rs @@ -0,0 +1,52 @@ +#![warn(clippy::useless_nonzero_new_unchecked)] + +use std::num::{NonZero, NonZeroUsize}; + +#[clippy::msrv = "1.83"] +const fn func() -> NonZeroUsize { + const { unsafe { NonZeroUsize::new_unchecked(3) } } + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context +} + +#[clippy::msrv = "1.82"] +const fn func_older() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_performance_hit_if_linted() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(x) } +} + +macro_rules! uns { + ($expr:expr) => { + unsafe { $expr } + }; +} + +macro_rules! nzu { + () => { + NonZeroUsize::new_unchecked(1) + }; +} + +fn main() { + const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + static _B: NonZero = unsafe { NonZero::::new_unchecked(42) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const AUX: usize = 3; + const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3)); + const _Y: NonZeroUsize = unsafe { nzu!() }; +} diff --git a/tests/ui/useless_nonzero_new_unchecked.stderr b/tests/ui/useless_nonzero_new_unchecked.stderr new file mode 100644 index 000000000000..adb146167633 --- /dev/null +++ b/tests/ui/useless_nonzero_new_unchecked.stderr @@ -0,0 +1,37 @@ +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:7:13 + | +LL | const { unsafe { NonZeroUsize::new_unchecked(3) } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + | + = note: `-D clippy::useless-nonzero-new-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_nonzero_new_unchecked)]` + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:37:30 + | +LL | const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + +error: `NonZero::::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:40:30 + | +LL | static _B: NonZero = unsafe { NonZero::::new_unchecked(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZero::::new(42).unwrap()` + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:43:32 + | +LL | const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + | + = note: the fixed expression does not require an `unsafe` context + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:47:30 + | +LL | const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(AUX).unwrap()` + +error: aborting due to 5 previous errors + From 44560cbd79cc3fc890e53609ffd3184787f620e7 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 13 Dec 2024 12:19:46 +0000 Subject: [PATCH 037/342] Add hir::HeaderSafety to make follow up commits simpler --- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/doc/missing_headers.rs | 2 +- clippy_lints/src/functions/misnamed_getters.rs | 2 +- clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs | 6 +++--- clippy_lints/src/inherent_to_string.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/ptr.rs | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 7c2f5efd8dd4..91ddbb44ff89 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -419,7 +419,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { id: LocalDefId, ) -> Self::Result { if let Some(header) = kind.header() - && header.safety.is_unsafe() + && header.is_unsafe() { ControlFlow::Break(()) } else { diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 3e2b7055de4d..8e2af6bf14a6 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -32,7 +32,7 @@ pub fn check( } let span = cx.tcx.def_span(owner_id); - match (headers.safety, sig.header.safety) { + match (headers.safety, sig.header.safety()) { (false, Safety::Unsafe) => span_lint( cx, MISSING_SAFETY_DOC, diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 017571c38db6..854fe144c291 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -34,7 +34,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: ImplicitSelfKind::None => return, }; - let name = if sig.header.safety.is_unsafe() { + let name = if sig.header.is_unsafe() { name.strip_suffix("_unchecked").unwrap_or(name) } else { name diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 3ded8dc30127..8a74951ef63e 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -20,8 +20,8 @@ pub(super) fn check_fn<'tcx>( def_id: LocalDefId, ) { let safety = match kind { - intravisit::FnKind::ItemFn(_, _, hir::FnHeader { safety, .. }) => safety, - intravisit::FnKind::Method(_, sig) => sig.header.safety, + intravisit::FnKind::ItemFn(_, _, header) => header.safety(), + intravisit::FnKind::Method(_, sig) => sig.header.safety(), intravisit::FnKind::Closure => return, }; @@ -31,7 +31,7 @@ pub(super) fn check_fn<'tcx>( pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); - check_raw_ptr(cx, sig.header.safety, sig.decl, body, item.owner_id.def_id); + check_raw_ptr(cx, sig.header.safety(), sig.decl, body, item.owner_id.def_id); } } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index e096dd251759..415b47adac5a 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // #11201 && let header = signature.header - && header.safety.is_safe() + && header.is_safe() && header.abi == Abi::Rust && impl_item.ident.name == sym::to_string && let decl = signature.decl diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 51351f6b7cd1..3965c4d40878 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5309,7 +5309,7 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr } const FN_HEADER: hir::FnHeader = hir::FnHeader { - safety: hir::Safety::Safe, + safety: hir::HeaderSafety::Normal(hir::Safety::Safe), constness: hir::Constness::NotConst, asyncness: hir::IsAsync::NotAsync, abi: rustc_target::spec::abi::Abi::Rust, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index abdce69e7645..688374b5676c 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { let name = impl_item.ident.name; let id = impl_item.owner_id; - if sig.header.safety.is_unsafe() { + if sig.header.is_unsafe() { // can't be implemented for unsafe new return; } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index a86926d8416c..506adf0f2cc5 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -541,7 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio .collect(); if let Some(args) = args && !args.is_empty() - && body.is_none_or(|body| sig.header.safety.is_unsafe() || contains_unsafe_block(cx, body.value)) + && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) { span_lint_and_then( cx, From 544f71f48d0b73db7d5815519dc6fd6a4ce87390 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Fri, 20 Dec 2024 22:29:06 +0900 Subject: [PATCH 038/342] add `manual_repeat_n` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/manual_repeat_n.rs | 43 +++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 26 +++++++++++++ clippy_utils/src/lib.rs | 1 + tests/ui/from_iter_instead_of_collect.fixed | 2 +- tests/ui/from_iter_instead_of_collect.rs | 2 +- tests/ui/manual_repeat_n.fixed | 30 ++++++++++++++ tests/ui/manual_repeat_n.rs | 30 ++++++++++++++ tests/ui/manual_repeat_n.stderr | 35 +++++++++++++++++ tests/ui/manual_str_repeat.fixed | 2 +- tests/ui/manual_str_repeat.rs | 2 +- tests/ui/slow_vector_initialization.fixed | 3 +- tests/ui/slow_vector_initialization.rs | 3 +- tests/ui/slow_vector_initialization.stderr | 26 ++++++------- 15 files changed, 188 insertions(+), 19 deletions(-) create mode 100644 clippy_lints/src/methods/manual_repeat_n.rs create mode 100644 tests/ui/manual_repeat_n.fixed create mode 100644 tests/ui/manual_repeat_n.rs create mode 100644 tests/ui/manual_repeat_n.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ceb08eeac3ff..cbac1a929df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5768,6 +5768,7 @@ Released 2018-09-13 [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid +[`manual_repeat_n`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain [`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5e6cfd94283c..cc0c9ec127c4 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -420,6 +420,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::MANUAL_IS_VARIANT_AND_INFO, crate::methods::MANUAL_NEXT_BACK_INFO, crate::methods::MANUAL_OK_OR_INFO, + crate::methods::MANUAL_REPEAT_N_INFO, crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, crate::methods::MANUAL_SPLIT_ONCE_INFO, crate::methods::MANUAL_STR_REPEAT_INFO, diff --git a/clippy_lints/src/methods/manual_repeat_n.rs b/clippy_lints/src/methods/manual_repeat_n.rs new file mode 100644 index 000000000000..6e09bf132aa1 --- /dev/null +++ b/clippy_lints/src/methods/manual_repeat_n.rs @@ -0,0 +1,43 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MANUAL_REPEAT_N; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + repeat_expr: &Expr<'_>, + take_arg: &Expr<'_>, + msrv: &Msrv, +) { + if msrv.meets(msrvs::REPEAT_N) + && !expr.span.from_expansion() + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind + && let Some(def_id) = fn_def_id(cx, repeat_expr) + && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) + && !expr_use_ctxt(cx, expr).is_ty_unified + && let Some(std_or_core) = std_or_core(cx) + { + let mut app = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MANUAL_REPEAT_N, + expr.span, + "this `repeat().take()` can be written more concisely", + "consider using `repeat_n()` instead", + format!( + "{std_or_core}::iter::repeat_n({}, {})", + snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet(cx, take_arg.span, "..") + ), + app, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d7e9f65bfa3a..b416d7d5814d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -58,6 +58,7 @@ mod manual_inspect; mod manual_is_variant_and; mod manual_next_back; mod manual_ok_or; +mod manual_repeat_n; mod manual_saturating_arithmetic; mod manual_str_repeat; mod manual_try_fold; @@ -4339,6 +4340,29 @@ declare_clippy_lint! { "using `NonZero::new_unchecked()` in a `const` context" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `repeat().take()` that can be replaced with `repeat_n()`. + /// + /// ### Why is this bad? + /// + /// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning. + /// + /// ### Example + /// ```no_run + /// let _ = std::iter::repeat(10).take(3); + /// ``` + /// Use instead: + /// ```no_run + /// let _ = std::iter::repeat_n(10, 3); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_REPEAT_N, + style, + "detect `repeat().take()` that can be replaced with `repeat_n()`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4506,6 +4530,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_MAP_OR, DOUBLE_ENDED_ITERATOR_LAST, USELESS_NONZERO_NEW_UNCHECKED, + MANUAL_REPEAT_N, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -5176,6 +5201,7 @@ impl Methods { ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [arg]) => { iter_out_of_bounds::check_take(cx, expr, recv, arg); + manual_repeat_n::check(cx, expr, recv, arg, &self.msrv); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a0de9a76a0ef..676f387a7f01 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -15,6 +15,7 @@ clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate, + clippy::manual_repeat_n, rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index c250162dfb8c..67da45a348f9 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] #![allow(unused_imports)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 8adbb841c8ba..423a7454bed7 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] #![allow(unused_imports)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; diff --git a/tests/ui/manual_repeat_n.fixed b/tests/ui/manual_repeat_n.fixed new file mode 100644 index 000000000000..4235b02a89e3 --- /dev/null +++ b/tests/ui/manual_repeat_n.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::manual_repeat_n)] + +use std::iter::repeat; + +fn main() { + let _ = std::iter::repeat_n(10, 3); + + let _ = std::iter::repeat_n(String::from("foo"), 4); + + for value in std::iter::repeat_n(5, 3) {} + + let _: Vec<_> = std::iter::repeat_n(String::from("bar"), 10).collect(); + + let _ = std::iter::repeat_n(vec![1, 2], 2); +} + +mod foo_lib { + pub fn iter() -> std::iter::Take> { + todo!() + } +} + +fn foo() { + let _ = match 1 { + 1 => foo_lib::iter(), + // Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`. + 2 => std::iter::repeat([1, 2].as_slice()).take(2), + _ => todo!(), + }; +} diff --git a/tests/ui/manual_repeat_n.rs b/tests/ui/manual_repeat_n.rs new file mode 100644 index 000000000000..dbf9ac6a14af --- /dev/null +++ b/tests/ui/manual_repeat_n.rs @@ -0,0 +1,30 @@ +#![warn(clippy::manual_repeat_n)] + +use std::iter::repeat; + +fn main() { + let _ = repeat(10).take(3); + + let _ = repeat(String::from("foo")).take(4); + + for value in std::iter::repeat(5).take(3) {} + + let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect(); + + let _ = repeat(vec![1, 2]).take(2); +} + +mod foo_lib { + pub fn iter() -> std::iter::Take> { + todo!() + } +} + +fn foo() { + let _ = match 1 { + 1 => foo_lib::iter(), + // Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`. + 2 => std::iter::repeat([1, 2].as_slice()).take(2), + _ => todo!(), + }; +} diff --git a/tests/ui/manual_repeat_n.stderr b/tests/ui/manual_repeat_n.stderr new file mode 100644 index 000000000000..87395b3f8bf1 --- /dev/null +++ b/tests/ui/manual_repeat_n.stderr @@ -0,0 +1,35 @@ +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:6:13 + | +LL | let _ = repeat(10).take(3); + | ^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(10, 3)` + | + = note: `-D clippy::manual-repeat-n` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_repeat_n)]` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:8:13 + | +LL | let _ = repeat(String::from("foo")).take(4); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("foo"), 4)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:10:18 + | +LL | for value in std::iter::repeat(5).take(3) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(5, 3)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:12:21 + | +LL | let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("bar"), 10)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:14:13 + | +LL | let _ = repeat(vec![1, 2]).take(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(vec![1, 2], 2)` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index 5f2f1bd9916d..da6f36f53b0d 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -1,4 +1,4 @@ -#![allow(non_local_definitions)] +#![allow(non_local_definitions, clippy::manual_repeat_n)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index 3e3c7f4db4a2..686ed4fee7d1 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -1,4 +1,4 @@ -#![allow(non_local_definitions)] +#![allow(non_local_definitions, clippy::manual_repeat_n)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; diff --git a/tests/ui/slow_vector_initialization.fixed b/tests/ui/slow_vector_initialization.fixed index 8c16bb307ca0..a8570366e646 100644 --- a/tests/ui/slow_vector_initialization.fixed +++ b/tests/ui/slow_vector_initialization.fixed @@ -1,4 +1,5 @@ -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] + use std::iter::repeat; fn main() { resize_vector(); diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 6831dad70b43..4b30fad409e3 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -1,4 +1,5 @@ -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] + use std::iter::repeat; fn main() { resize_vector(); diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index 7f4b9f7b67a4..4a25cafcddf2 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -1,5 +1,5 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:13:20 + --> tests/ui/slow_vector_initialization.rs:14:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -11,7 +11,7 @@ LL | | vec1.extend(repeat(0).take(len)); = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:19:20 + --> tests/ui/slow_vector_initialization.rs:20:20 | LL | let mut vec2 = Vec::with_capacity(len - 10); | ____________________^ @@ -20,7 +20,7 @@ LL | | vec2.extend(repeat(0).take(len - 10)); | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:27:20 + --> tests/ui/slow_vector_initialization.rs:28:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -29,7 +29,7 @@ LL | | vec4.extend(repeat(0).take(vec4.capacity())); | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:38:27 + --> tests/ui/slow_vector_initialization.rs:39:27 | LL | let mut resized_vec = Vec::with_capacity(30); | ___________________________^ @@ -38,7 +38,7 @@ LL | | resized_vec.resize(30, 0); | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:42:26 + --> tests/ui/slow_vector_initialization.rs:43:26 | LL | let mut extend_vec = Vec::with_capacity(30); | __________________________^ @@ -47,7 +47,7 @@ LL | | extend_vec.extend(repeat(0).take(30)); | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:50:20 + --> tests/ui/slow_vector_initialization.rs:51:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -56,7 +56,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:59:20 + --> tests/ui/slow_vector_initialization.rs:60:20 | LL | let mut vec3 = Vec::with_capacity(len - 10); | ____________________^ @@ -65,7 +65,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:63:20 + --> tests/ui/slow_vector_initialization.rs:64:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -74,7 +74,7 @@ LL | | vec4.resize(vec4.capacity(), 0); | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:68:12 + --> tests/ui/slow_vector_initialization.rs:69:12 | LL | vec1 = Vec::with_capacity(10); | ____________^ @@ -83,7 +83,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:76:20 + --> tests/ui/slow_vector_initialization.rs:77:20 | LL | let mut vec1 = Vec::new(); | ____________________^ @@ -92,7 +92,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:81:20 + --> tests/ui/slow_vector_initialization.rs:82:20 | LL | let mut vec3 = Vec::new(); | ____________________^ @@ -101,7 +101,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:86:12 + --> tests/ui/slow_vector_initialization.rs:87:12 | LL | vec1 = Vec::new(); | ____________^ @@ -110,7 +110,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:90:12 + --> tests/ui/slow_vector_initialization.rs:91:12 | LL | vec1 = vec![]; | ____________^ From 9a1bbe91bc0fd18235ba8e243fc121ab8270febd Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Sat, 21 Dec 2024 01:17:50 +0900 Subject: [PATCH 039/342] use `repeat_n()` where available --- clippy_lints/src/doc/lazy_continuation.rs | 2 +- clippy_utils/src/lib.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs index f9e4a43c0e7a..d6ab9ac6749b 100644 --- a/clippy_lints/src/doc/lazy_continuation.rs +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -57,7 +57,7 @@ pub(super) fn check( diag.span_suggestion_verbose( span.shrink_to_hi(), "indent this line", - std::iter::repeat(" ").take(indent).join(""), + std::iter::repeat_n(" ", indent).join(""), Applicability::MaybeIncorrect, ); diag.help("if this is supposed to be its own paragraph, add a blank line"); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 676f387a7f01..25c55aade15d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -15,7 +15,6 @@ clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate, - clippy::manual_repeat_n, rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic )] @@ -89,7 +88,7 @@ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::iter::{once, repeat}; +use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; @@ -3421,7 +3420,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St })) .join("::") } else { - repeat(String::from("super")).take(go_up_by).chain(path).join("::") + repeat_n(String::from("super"), go_up_by).chain(path).join("::") } } From 321d21ffd624f161117d89557d16114b4d27da22 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 1 Jan 2025 19:09:01 +0100 Subject: [PATCH 040/342] allowed_through_unstable_modules: support showing a deprecation message when the unstable module name is used --- clippy_lints/src/std_instead_of_core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 82ff13a5aff1..8ec7bfe9edd3 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -180,7 +180,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool { if let Some(stability) = cx.tcx.lookup_stability(def_id) && let StabilityLevel::Stable { since, - allowed_through_unstable_modules: false, + allowed_through_unstable_modules: None, } = stability.level { let stable = match since { From caebd67ba99e15ffd0854804d19a3b9d5e4bbc72 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 1 Jan 2025 20:22:59 +0100 Subject: [PATCH 041/342] intrinsics: deprecate calling them via the unstable std::intrinsics path --- tests/ui/std_instead_of_core.fixed | 2 +- tests/ui/std_instead_of_core.rs | 2 +- tests/ui/transmute.rs | 22 +++++++++--------- tests/ui/transmute.stderr | 36 +++++++++++++++--------------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 227b98c683e9..ec158ee02de8 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] -#![allow(unused_imports)] +#![allow(unused_imports, deprecated)] extern crate alloc; diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 01bb78dd3bf1..9c3c1658d8fe 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::std_instead_of_core)] -#![allow(unused_imports)] +#![allow(unused_imports, deprecated)] extern crate alloc; diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index eeea3f080b1c..7f5bdea4acf3 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -24,31 +24,31 @@ fn my_vec() -> MyVec { #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { // FIXME: should lint - // let _: &'a T = core::intrinsics::transmute(t); + // let _: &'a T = core::mem::transmute(t); - let _: &'a U = core::intrinsics::transmute(t); + let _: &'a U = core::mem::transmute(t); - let _: *const T = core::intrinsics::transmute(t); + let _: *const T = core::mem::transmute(t); //~^ ERROR: transmute from a reference to a pointer //~| NOTE: `-D clippy::useless-transmute` implied by `-D warnings` - let _: *mut T = core::intrinsics::transmute(t); + let _: *mut T = core::mem::transmute(t); //~^ ERROR: transmute from a reference to a pointer - let _: *const U = core::intrinsics::transmute(t); + let _: *const U = core::mem::transmute(t); //~^ ERROR: transmute from a reference to a pointer } #[warn(clippy::useless_transmute)] fn useless() { unsafe { - let _: Vec = core::intrinsics::transmute(my_vec()); + let _: Vec = core::mem::transmute(my_vec()); //~^ ERROR: transmute from a type (`std::vec::Vec`) to itself let _: Vec = core::mem::transmute(my_vec()); //~^ ERROR: transmute from a type (`std::vec::Vec`) to itself - let _: Vec = std::intrinsics::transmute(my_vec()); + let _: Vec = std::mem::transmute(my_vec()); //~^ ERROR: transmute from a type (`std::vec::Vec`) to itself let _: Vec = std::mem::transmute(my_vec()); @@ -94,17 +94,17 @@ fn crosspointer() { let int_mut_ptr: *mut Usize = &mut int as *mut Usize; unsafe { - let _: Usize = core::intrinsics::transmute(int_const_ptr); + let _: Usize = core::mem::transmute(int_const_ptr); //~^ ERROR: transmute from a type (`*const Usize`) to the type that it points to ( //~| NOTE: `-D clippy::crosspointer-transmute` implied by `-D warnings` - let _: Usize = core::intrinsics::transmute(int_mut_ptr); + let _: Usize = core::mem::transmute(int_mut_ptr); //~^ ERROR: transmute from a type (`*mut Usize`) to the type that it points to (`U - let _: *const Usize = core::intrinsics::transmute(my_int()); + let _: *const Usize = core::mem::transmute(my_int()); //~^ ERROR: transmute from a type (`Usize`) to a pointer to that type (`*const Usi - let _: *mut Usize = core::intrinsics::transmute(my_int()); + let _: *mut Usize = core::mem::transmute(my_int()); //~^ ERROR: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize } } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 41a10f381dc5..b5032772856e 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,8 +1,8 @@ error: transmute from a reference to a pointer --> tests/ui/transmute.rs:31:23 | -LL | let _: *const T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` +LL | let _: *const T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` | = note: `-D clippy::useless-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` @@ -10,20 +10,20 @@ LL | let _: *const T = core::intrinsics::transmute(t); error: transmute from a reference to a pointer --> tests/ui/transmute.rs:35:21 | -LL | let _: *mut T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` +LL | let _: *mut T = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer --> tests/ui/transmute.rs:38:23 | -LL | let _: *const U = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +LL | let _: *const U = core::mem::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself --> tests/ui/transmute.rs:45:27 | -LL | let _: Vec = core::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Vec = core::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself --> tests/ui/transmute.rs:48:27 @@ -34,8 +34,8 @@ LL | let _: Vec = core::mem::transmute(my_vec()); error: transmute from a type (`std::vec::Vec`) to itself --> tests/ui/transmute.rs:51:27 | -LL | let _: Vec = std::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Vec = std::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself --> tests/ui/transmute.rs:54:27 @@ -64,8 +64,8 @@ LL | let _: *const usize = std::mem::transmute(1 + 1usize); error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) --> tests/ui/transmute.rs:97:24 | -LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Usize = core::mem::transmute(int_const_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` @@ -73,20 +73,20 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) --> tests/ui/transmute.rs:101:24 | -LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Usize = core::mem::transmute(int_mut_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) --> tests/ui/transmute.rs:104:31 | -LL | let _: *const Usize = core::intrinsics::transmute(my_int()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: *const Usize = core::mem::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) --> tests/ui/transmute.rs:107:29 | -LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: *mut Usize = core::mem::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` --> tests/ui/transmute.rs:114:28 From 7485970298ae411d2298680071b95754a6e8ea1c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 15 Jan 2025 21:08:43 +0100 Subject: [PATCH 042/342] Pause assignments for @xFrednet --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index eadfd7107c77..3d35116ebc17 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -29,7 +29,6 @@ users_on_vacation = [ "*" = [ "@Manishearth", "@llogiq", - "@xFrednet", "@Alexendoo", "@dswij", "@Jarcho", From ded9354dcf76aaa2cd2164a8b556f61c2f188d79 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 11 Jan 2025 15:14:31 +0100 Subject: [PATCH 043/342] Suggest using `Vec::extend()` in `same_item_push` Using `Vec::extend(std::iter::repeat_n(item, N))` allows to use the more natural number of elements to add `N`, as is probably done in the original loop, instead of computing the difference between the existing number of elements and the wanted one. Before MSRV 1.82, the older suggestion to use `Vec::resize()` is still issued. --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/same_item_push.rs | 34 ++++++++++++++-------- tests/ui/same_item_push.rs | 20 +++++++++---- tests/ui/same_item_push.stderr | 36 ++++++++++++++++-------- 6 files changed, 66 insertions(+), 28 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 181e794e6e46..a3e10088db26 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -767,6 +767,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) +* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index c616589c56e0..9bf6c675f000 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -636,6 +636,7 @@ define_Conf! { ptr_as_ptr, redundant_field_names, redundant_static_lifetimes, + same_item_push, seek_from_current, seek_rewind, transmute_ptr_to_ref, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index f3ca4a4a5715..c5e75af2303c 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -830,7 +830,7 @@ impl Loops { for_kv_map::check(cx, pat, arg, body); mut_range_bound::check(cx, arg, body); single_element_loop::check(cx, pat, arg, body, expr); - same_item_push::check(cx, pat, arg, body, expr); + same_item_push::check(cx, pat, arg, body, expr, &self.msrv); manual_flatten::check(cx, pat, arg, body, span); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 951ebc9caef0..c27e930c99a5 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,8 +1,9 @@ use super::SAME_ITEM_PUSH; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_to_local; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{msrvs, path_to_local, std_or_core}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -19,19 +20,30 @@ pub(super) fn check<'tcx>( _: &'tcx Expr<'_>, body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, + msrv: &Msrv, ) { - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: &Msrv) { let mut app = Applicability::Unspecified; let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0; let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0; - span_lint_and_help( + let secondary_help = if msrv.meets(msrvs::REPEAT_N) + && let Some(std_or_core) = std_or_core(cx) + { + format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`") + } else { + format!("or `{vec_str}.resize(NEW_SIZE, {item_str})`") + }; + + span_lint_and_then( cx, SAME_ITEM_PUSH, vec.span, - "it looks like the same item is being pushed into this Vec", - None, - format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), + "it looks like the same item is being pushed into this `Vec`", + |diag| { + diag.help(format!("consider using `vec![{item_str};SIZE]`")) + .help(secondary_help); + }, ); } @@ -67,11 +79,11 @@ pub(super) fn check<'tcx>( { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item, ctxt); + emit_lint(cx, vec, pushed_item, ctxt, msrv); } }, _ => {}, @@ -79,11 +91,11 @@ pub(super) fn check<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), _ => {}, } }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), _ => {}, } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index df9c2817f508..87fd59ad3179 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -21,33 +21,43 @@ fn main() { let item = 2; for _ in 5..=20 { vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec: Vec = Vec::new(); for _ in 0..15 { let item = 2; vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec: Vec = Vec::new(); for _ in 0..15 { vec.push(13); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec = Vec::new(); for _ in 0..20 { vec.push(VALUE); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec = Vec::new(); let item = VALUE; for _ in 0..20 { vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` + } + + #[clippy::msrv = "1.81"] + fn older_msrv() { + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + //~^ ERROR: it looks like the same item is being pushed into this `Vec` + } } // ** non-linted cases ** diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index eb296ed4ce4a..e3fa4f9cbcec 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,44 +1,58 @@ -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:23:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` = note: `-D clippy::same-item-push` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::same_item_push)]` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:30:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:36:9 | LL | vec.push(13); | ^^^ | - = help: consider using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) + = help: consider using `vec![13;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(13, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:42:9 | LL | vec.push(VALUE); | ^^^ | - = help: consider using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) + = help: consider using `vec![VALUE;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(VALUE, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:49:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` -error: aborting due to 5 previous errors +error: it looks like the same item is being pushed into this `Vec` + --> tests/ui/same_item_push.rs:58:13 + | +LL | vec.push(item); + | ^^^ + | + = help: consider using `vec![item;SIZE]` + = help: or `vec.resize(NEW_SIZE, item)` + +error: aborting due to 6 previous errors From 53238c3db6b293546f0b3bbd835ca85517ae9d8f Mon Sep 17 00:00:00 2001 From: Flakebi Date: Thu, 2 Jan 2025 13:15:21 +0100 Subject: [PATCH 044/342] Target option to require explicit cpu Some targets have many different CPUs and no generic CPU that can be used as a default. For these targets, the user needs to explicitly specify a CPU through `-C target-cpu=`. Add an option for targets and an error message if no CPU is set. This affects the proposed amdgpu and avr targets. --- compiler/rustc_codegen_ssa/messages.ftl | 2 ++ compiler/rustc_codegen_ssa/src/base.rs | 5 +++++ compiler/rustc_codegen_ssa/src/errors.rs | 4 ++++ compiler/rustc_target/src/spec/json.rs | 2 ++ compiler/rustc_target/src/spec/mod.rs | 4 ++++ .../run-make/target-specs/require-explicit-cpu.json | 11 +++++++++++ tests/run-make/target-specs/rmake.rs | 13 +++++++++++++ 7 files changed, 41 insertions(+) create mode 100644 tests/run-make/target-specs/require-explicit-cpu.json diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 56188714b44f..60e97910e197 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -30,6 +30,8 @@ codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error} +codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C target-cpu` + codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} codegen_ssa_dlltool_fail_import_library = diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 77e1fed720df..9c31cde259f0 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -616,6 +616,11 @@ pub fn codegen_crate( return ongoing_codegen; } + if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { + // The target has no default cpu, but none is set explicitly + tcx.dcx().emit_fatal(errors::CpuRequired); + } + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); // Run the monomorphization collector and partition the collected items into diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index c7213bbc801f..7132219fb3dc 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -491,6 +491,10 @@ pub(crate) struct CheckInstalledVisualStudio; #[diag(codegen_ssa_insufficient_vs_code_product)] pub(crate) struct InsufficientVSCodeProduct; +#[derive(Diagnostic)] +#[diag(codegen_ssa_cpu_required)] +pub(crate) struct CpuRequired; + #[derive(Diagnostic)] #[diag(codegen_ssa_processing_dymutil_failed)] #[note] diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 9cdc0801b1f0..015ea97f2acb 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -546,6 +546,7 @@ impl Target { key!(link_env_remove, list); key!(asm_args, list); key!(cpu); + key!(need_explicit_cpu, bool); key!(features); key!(dynamic_linking, bool); key!(direct_access_external_data, Option); @@ -720,6 +721,7 @@ impl ToJson for Target { target_option_val!(link_env_remove); target_option_val!(asm_args); target_option_val!(cpu); + target_option_val!(need_explicit_cpu); target_option_val!(features); target_option_val!(dynamic_linking); target_option_val!(direct_access_external_data); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 02962d55a60e..b017145daa39 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2240,6 +2240,9 @@ pub struct TargetOptions { /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults /// to "generic". pub cpu: StaticCow, + /// Whether a cpu needs to be explicitly set. + /// Set to true if there is no default cpu. Defaults to false. + pub need_explicit_cpu: bool, /// Default target features to pass to LLVM. These features overwrite /// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`. /// Corresponds to `llc -mattr=$features`. @@ -2676,6 +2679,7 @@ impl Default for TargetOptions { link_script: None, asm_args: cvs![], cpu: "generic".into(), + need_explicit_cpu: false, features: "".into(), direct_access_external_data: None, dynamic_linking: false, diff --git a/tests/run-make/target-specs/require-explicit-cpu.json b/tests/run-make/target-specs/require-explicit-cpu.json new file mode 100644 index 000000000000..5cbb9573b3f1 --- /dev/null +++ b/tests/run-make/target-specs/require-explicit-cpu.json @@ -0,0 +1,11 @@ +{ + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + "linker-flavor": "gcc", + "llvm-target": "i686-unknown-linux-gnu", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "arch": "x86", + "os": "linux", + "need-explicit-cpu": true +} diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 79c888ab3406..f36a5784c893 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -63,4 +63,17 @@ fn main() { .crate_type("lib") .run_fail() .assert_stderr_contains("data-layout for target"); + rustc() + .input("foo.rs") + .target("require-explicit-cpu") + .crate_type("lib") + .run_fail() + .assert_stderr_contains("target requires explicitly specifying a cpu"); + rustc() + .input("foo.rs") + .target("require-explicit-cpu") + .crate_type("lib") + .arg("-Ctarget-cpu=generic") + .run(); + rustc().target("require-explicit-cpu").arg("--print=target-cpus").run(); } From c1790b14bcd62df7bccdfa7d7fbe4533dfdcdc8c Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 14 Jan 2025 11:40:22 +0530 Subject: [PATCH 045/342] uefi: Implement path UEFI paths can be of 4 types: 1. Absolute Shell Path: Uses shell mappings 2. Absolute Device Path: this is what we want 3: Relative root: path relative to the current root. 4: Relative Absolute shell path can be identified with `:` and Absolute Device path can be identified with `/`. Relative root path will start with `\`. The algorithm is mostly taken from edk2 UEFI shell implementation and is somewhat simple. Check for the path type in order. For Absolute Shell path, use `EFI_SHELL->GetDevicePathFromMap` to get a BorrowedDevicePath for the volume. For Relative paths, we use the current working directory to construct the new path. BorrowedDevicePath abstraction is needed to interact with `EFI_SHELL->GetDevicePathFromMap` which returns a Device Path Protocol with the lifetime of UEFI shell. Absolute Shell paths cannot exist if UEFI shell is missing. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/helpers.rs | 50 ++++++++++- library/std/src/sys/path/mod.rs | 8 +- library/std/src/sys/path/uefi.rs | 105 ++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 library/std/src/sys/path/uefi.rs diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 7504a0f7ad7f..dccc137d6f56 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -14,10 +14,12 @@ use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; +use crate::marker::PhantomData; use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; +use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -278,6 +280,10 @@ impl OwnedDevicePath { pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } + + pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.0) + } } impl Drop for OwnedDevicePath { @@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath { impl crate::fmt::Debug for OwnedDevicePath { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { - match device_path_to_text(self.0) { + match self.borrow().to_text() { Ok(p) => p.fmt(f), Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), } } } +pub(crate) struct BorrowedDevicePath<'a> { + protocol: NonNull, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> BorrowedDevicePath<'a> { + pub(crate) const fn new(protocol: NonNull) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) fn to_text(&self) -> io::Result { + device_path_to_text(self.protocol) + } +} + +impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(), + } + } +} + pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, @@ -452,3 +482,21 @@ pub(crate) fn open_shell() -> Option> { None } + +/// Get device path protocol associated with shell mapping. +/// +/// returns None in case no such mapping is exists +pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result> { + let shell = + open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?; + let mut path = os_string_to_raw(map.as_os_str()) + .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?; + + // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not + // freed throughout it's lifetime. So it has a 'static lifetime. + let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) }; + let protocol = NonNull::new(protocol) + .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?; + + Ok(BorrowedDevicePath::new(protocol)) +} diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 24a94ec78282..1fa4e80d6780 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -5,12 +5,12 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use sgx::*; - } else if #[cfg(any( - target_os = "uefi", - target_os = "solid_asp3", - ))] { + } else if #[cfg(target_os = "solid_asp3")] { mod unsupported_backslash; pub use unsupported_backslash::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unix; pub use unix::*; diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs new file mode 100644 index 000000000000..a3f4a3bfe1b6 --- /dev/null +++ b/library/std/src/sys/path/uefi.rs @@ -0,0 +1,105 @@ +#![forbid(unsafe_op_in_unsafe_fn)] +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::{helpers, unsupported_err}; + +const FORWARD_SLASH: u8 = b'/'; +const COLON: u8 = b':'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_: &OsStr) -> Option> { + None +} + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +/// UEFI paths can be of 4 types: +/// +/// 1. Absolute Shell Path: Uses shell mappings (eg: `FS0:`). Does not exist if UEFI shell not present. +/// It can be identified with `:`. +/// Eg: FS0:\abc\run.efi +/// +/// 2. Absolute Device Path: this is what we want +/// It can be identified with `/`. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +/// +/// 3: Relative root: path relative to the current volume. +/// It will start with `\`. +/// Eg: \abc\run.efi +/// +/// 4: Relative +/// Eg: run.efi +/// +/// The algorithm is mostly taken from edk2 UEFI shell implementation and is +/// somewhat simple. Check for the path type in order. +/// +/// The volume mapping in Absolute Shell Path (not the rest of the path) can be converted to Device +/// Path Protocol using `EFI_SHELL->GetDevicePathFromMap`. The rest of the path (Relative root +/// path), can just be appended to the remaining path. +/// +/// For Relative root, we get the current volume (either in Shell Mapping, or Device Path Protocol +/// form) and join it with the relative root path. We then recurse the function to resolve the Shell +/// Mapping if present. +/// +/// For Relative paths, we use the current working directory to construct +/// the new path and recurse the function to resolve the Shell mapping if present. +/// +/// Finally, at the end, we get the 2nd form, i.e. Absolute Device Path, which can be used in the +/// normal UEFI APIs such as file, process, etc. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +pub(crate) fn absolute(path: &Path) -> io::Result { + // Absolute Shell Path + if path.as_os_str().as_encoded_bytes().contains(&COLON) { + let mut path_components = path.components(); + // Since path is not empty, it has at least one Component + let prefix = path_components.next().unwrap(); + + let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?; + let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?; + + // UEFI Shell does not seem to end device path with `/` + if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH { + dev_path_text.push("/"); + } + + let mut ans = PathBuf::from(dev_path_text); + ans.push(path_components); + + return Ok(ans); + } + + // Absolute Device Path + if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) { + return Ok(path.to_path_buf()); + } + + // cur_dir() always returns something + let cur_dir = crate::env::current_dir().unwrap(); + let mut path_components = path.components(); + + // Relative Root + if path_components.next().unwrap() == crate::path::Component::RootDir { + let mut ans = PathBuf::new(); + ans.push(cur_dir.components().next().unwrap()); + ans.push(path_components); + return absolute(&ans); + } + + absolute(&cur_dir.join(path)) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + let temp = path.as_os_str().as_encoded_bytes(); + temp.contains(&COLON) || temp.contains(&FORWARD_SLASH) +} From ee522d8d15506425911dcead1035e66eec4328a0 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Fri, 17 Jan 2025 15:37:48 +0900 Subject: [PATCH 046/342] change literal_string_with_formatting_args lint to pedantic --- clippy_lints/src/literal_string_with_formatting_args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/literal_string_with_formatting_args.rs b/clippy_lints/src/literal_string_with_formatting_args.rs index d9de784873bc..9365896e1a2e 100644 --- a/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.85.0"] pub LITERAL_STRING_WITH_FORMATTING_ARGS, - suspicious, + pedantic, "Checks if string literals have formatting arguments" } From e307b2f0c60e230dd1d57ba7458b2e0e339f3655 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Fri, 17 Jan 2025 18:16:40 +0900 Subject: [PATCH 047/342] fix version search in the website --- util/gh-pages/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index c2197b89c566..34d76ad642ec 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -341,8 +341,8 @@ window.filters = { || !filters.levels_filter[lint.level] || !filters.applicabilities_filter[lint.applicability] || !(filters.version_filter["="] === null || lint.version === filters.version_filter["="]) - || !(filters.version_filter["≥"] === null || lint.version > filters.version_filter["≥"]) - || !(filters.version_filter["≤"] === null || lint.version < filters.version_filter["≤"]) + || !(filters.version_filter["≥"] === null || lint.version >= filters.version_filter["≥"]) + || !(filters.version_filter["≤"] === null || lint.version <= filters.version_filter["≤"]) ); if (lint.filteredOut || lint.searchFilteredOut) { lint.elem.style.display = "none"; From 23e602cd94efecf2136a29e77d335aedf9ffea74 Mon Sep 17 00:00:00 2001 From: Vishruth-Thimmaiah Date: Fri, 17 Jan 2025 23:19:43 +0530 Subject: [PATCH 048/342] fix: correct suggestion for significant_drop_in_scrutinee in expressions fixes: #13986 --- .../matches/significant_drop_in_scrutinee.rs | 4 ++-- tests/ui/significant_drop_in_scrutinee.rs | 14 ++++++++++++++ tests/ui/significant_drop_in_scrutinee.stderr | 18 +++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 9d8e0a694339..35f2e780d2e2 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{get_attr, is_lint_allowed}; use itertools::Itertools; @@ -152,7 +152,7 @@ fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: & diag.multipart_suggestion( suggestion_message, vec![ - (expr.span.shrink_to_lo(), replacement), + (first_line_of_span(cx, expr.span).shrink_to_lo(), replacement), (found.found_span, scrutinee_replacement), ], Applicability::MaybeIncorrect, diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index e9ebd23beac3..39d550398d70 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -850,4 +850,18 @@ async fn should_not_trigger_lint_in_async_expansion(mutex: Mutex) -> i32 { } } +fn should_trigger_lint_in_match_expr() { + let mutex = Mutex::new(State {}); + + // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it + // is preserved until the end of the match, but there is no clear indication that this is the + // case. + let _ = match mutex.lock().unwrap().foo() { + //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + true => 0, + false => 1, + }; +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index 23e38948ec59..f99d862aa6b2 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -584,5 +584,21 @@ LL ~ let value = *foo_async(&mutex).await.unwrap(); LL ~ match value { | -error: aborting due to 30 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:859:19 + | +LL | let _ = match mutex.lock().unwrap().foo() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().foo(); +LL ~ let _ = match value { + | + +error: aborting due to 31 previous errors From 3f4aa9bf6e3816408c8a516d57010b5ae70728fe Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Wed, 11 Dec 2024 12:59:58 +0500 Subject: [PATCH 049/342] Update doc wildcard_dependencies --- clippy_lints/src/cargo/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 96a2b1614646..60371dcd7715 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -161,6 +161,15 @@ declare_clippy_lint! { /// [dependencies] /// regex = "*" /// ``` + /// Use instead: + /// ```toml + /// [dependencies] + /// # allow patch updates, but not minor or major version changes + /// some_crate_1 = "~1.2.3" + /// + /// # pin the version to a specific version + /// some_crate_2 = "=1.2.3" + /// ``` #[clippy::version = "1.32.0"] pub WILDCARD_DEPENDENCIES, cargo, From f32764070683aa1f7aec5771f8a6352c3dd2589e Mon Sep 17 00:00:00 2001 From: Thomas Churchman Date: Mon, 16 Dec 2024 15:40:33 +0100 Subject: [PATCH 050/342] Emit `missing_const_for_fn` for `CONST_MUT_REFS` --- clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 7 ++++--- tests/ui/missing_const_for_fn/cant_be_const.rs | 6 ++++++ .../ui/missing_const_for_fn/could_be_const.fixed | 5 +++++ tests/ui/missing_const_for_fn/could_be_const.rs | 5 +++++ .../missing_const_for_fn/could_be_const.stderr | 16 +++++++++++++++- 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index eeb96a6ccb80..d9ef817306ce 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -18,7 +18,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_UNWRAP } + 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } 1,80,0 { BOX_INTO_ITER } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 428b40c5771c..3f190377fad6 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -29,13 +29,14 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) let def_id = body.source.def_id(); for local in &body.local_decls { - check_ty(tcx, local.ty, local.source_info.span)?; + check_ty(tcx, local.ty, local.source_info.span, msrv)?; } // impl trait is gone in MIR, so check the return type manually check_ty( tcx, tcx.fn_sig(def_id).instantiate_identity().output().skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, + msrv, )?; for bb in &*body.basic_blocks { @@ -51,7 +52,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) Ok(()) } -fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, msrv: &Msrv) -> McfResult { for arg in ty.walk() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -62,7 +63,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { }; match ty.kind() { - ty::Ref(_, _, hir::Mutability::Mut) => { + ty::Ref(_, _, hir::Mutability::Mut) if !msrv.meets(msrvs::CONST_MUT_REFS) => { return Err((span, "mutable references in const fn are unstable".into())); }, ty::Alias(ty::Opaque, ..) => return Err((span, "`impl Trait` in const fn is unstable".into())), diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index d2f9e34a5ceb..ce7d9a287a12 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -220,3 +220,9 @@ mod with_ty_alias { let _: Foo = 1; } } + +// Do not lint because mutable references in const functions are unstable in 1.82 +#[clippy::msrv = "1.82"] +fn mut_add(x: &mut i32) { + *x += 1; +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.fixed b/tests/ui/missing_const_for_fn/could_be_const.fixed index 014fbb85c7a3..29a7bcf4319a 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -212,3 +212,8 @@ mod extern_fn { const extern "system-unwind" fn system_unwind() {} //~^ ERROR: this could be a `const fn` } + +const fn mut_add(x: &mut i32) { + //~^ ERROR: this could be a `const fn` + *x += 1; +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 4f7c2cbcf0b4..1450c2da7077 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -212,3 +212,8 @@ mod extern_fn { extern "system-unwind" fn system_unwind() {} //~^ ERROR: this could be a `const fn` } + +fn mut_add(x: &mut i32) { + //~^ ERROR: this could be a `const fn` + *x += 1; +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index cc7dfd0888d0..2049d6fbc236 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -316,5 +316,19 @@ help: make the function `const` LL | const extern "system-unwind" fn system_unwind() {} | +++++ -error: aborting due to 24 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:216:1 + | +LL | / fn mut_add(x: &mut i32) { +LL | | +LL | | *x += 1; +LL | | } + | |_^ + | +help: make the function `const` + | +LL | const fn mut_add(x: &mut i32) { + | +++++ + +error: aborting due to 25 previous errors From a487c601a4433cd116a1def63eb8569089ebbf2a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 18 Jan 2025 15:44:05 +0100 Subject: [PATCH 051/342] Fix typo in `check_clousure` function name --- clippy_lints/src/eta_reduction.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index c0b4743fd71c..6cba6e2e9c70 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -76,20 +76,20 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind { for arg in args { - check_clousure(cx, Some(receiver), arg); + check_closure(cx, Some(receiver), arg); } } if let ExprKind::Call(func, args) = expr.kind { - check_clousure(cx, None, func); + check_closure(cx, None, func); for arg in args { - check_clousure(cx, None, arg); + check_closure(cx, None, arg); } } } } #[allow(clippy::too_many_lines)] -fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { +fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { let body = if let ExprKind::Closure(c) = expr.kind && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) From 4a7ec72961662ebfef0d315d66b0e9006219cea2 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Sat, 18 Jan 2025 17:38:24 +0100 Subject: [PATCH 052/342] Make niches into nices --- .../crates/ide/src/hover/render.rs | 96 ++++++++++++++++++- .../crates/ide/src/hover/tests.rs | 2 +- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 46242b75dd0b..7fe344cfa426 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1082,7 +1082,19 @@ fn render_memory_layout( if config.niches { if let Some(niches) = layout.niches() { - format_to!(label, "niches = {niches}, "); + if niches > 1024 { + if is_pwr2(niches) { + format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); + } else if is_pwr2plus1(niches) { + format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); + } else if is_pwr2minus1(niches) { + format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); + } else { + format_to!(label, "niches = really rather quite large, "); + } + } else { + format_to!(label, "niches = {niches}, "); + } } } label.pop(); // ' ' @@ -1210,3 +1222,85 @@ fn render_dyn_compatibility( } } } + +fn is_pwr2(val: u128) -> bool { + val.count_ones() == 1 +} + +fn is_pwr2minus1(val: u128) -> bool { + val == u128::MAX || (val + 1).count_ones() == 1 +} + +fn is_pwr2plus1(val: u128) -> bool { + val != 0 && (val - 1).count_ones() == 1 +} + +fn pwr2_to_exponent(num: u128) -> String { + const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; + (127 - num.leading_zeros()) + .to_string() + .chars() + .map(|c| c.to_digit(10).unwrap() as usize) + .map(|idx| DIGITS[idx]) + .collect::() +} + +#[cfg(test)] +mod tests { + use super::*; + + const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX]; + + #[test] + fn test_is_pwr2() { + const OUTCOMES: [bool; 10] = + [false, true, true, false, true, false, true, false, false, false]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2(*test); + assert_eq!(actual, expected, "is_pwr2({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_is_pwr2minus1() { + const OUTCOMES: [bool; 10] = + [true, true, false, true, false, true, false, false, false, true]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2minus1(*test); + assert_eq!(actual, expected, "is_pwr2minu1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_is_pwr2plus1() { + const OUTCOMES: [bool; 10] = + [false, false, true, true, false, false, false, true, false, false]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2plus1(*test); + assert_eq!(actual, expected, "is_pwr2plus1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_pwr2_to_exponent() { + const TESTERS: [u128; 9] = [ + 1, + 2, + 4, + 8, + 16, + 9223372036854775808, + 18446744073709551616, + 36893488147419103232, + 170141183460469231731687303715884105728, + ]; + const OUTCOMES: [&str; 9] = ["⁰", "¹", "²", "³", "⁴", "⁶³", "⁶⁴", "⁶⁵", "¹²⁷"]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = pwr2_to_exponent(*test); + assert_eq!( + actual, expected, + "pwr2_to_exponent({test}) returned {actual}, expected {expected}", + ); + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 014b751f95b0..63eb772fde23 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1357,7 +1357,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = 4294967288 + size = 12 (0xC), align = 4, niches = really rather quite large "#]], ); } From e7f1e421b5d7fd888f8048e01eef1a654cf7760e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sat, 18 Jan 2025 22:08:38 +0000 Subject: [PATCH 053/342] Revert "Auto merge of #134330 - scottmcm:no-more-rvalue-len, r=matthewjasper" This reverts commit e108481f74ff123ad98a63bd107a18d13035b275, reversing changes made to 303e8bd768526a5812bb1776e798e829ddb7d3ca. --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 428b40c5771c..104ae154e369 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -109,7 +109,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(tcx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv), From fbf66310c1ac3869ca4d3628191908697bbd6c96 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 19 Jan 2025 12:38:59 +0900 Subject: [PATCH 054/342] chore: change to nursery instead --- clippy_lints/src/literal_string_with_formatting_args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/literal_string_with_formatting_args.rs b/clippy_lints/src/literal_string_with_formatting_args.rs index 9365896e1a2e..a957c0e22a29 100644 --- a/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.85.0"] pub LITERAL_STRING_WITH_FORMATTING_ARGS, - pedantic, + nursery, "Checks if string literals have formatting arguments" } From 7f37b2af97c921b0dc1d03564538048114434323 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 14 Jan 2025 00:21:16 +0100 Subject: [PATCH 055/342] Use clearer multipart suggestions for `unnecessary_map_or` lint A multipart suggestion will be used whenever the method call can be replaced by another one with the first argument removed. It helps place the new method call in context, especially when it is part of a larger expression. --- clippy_lints/src/methods/mod.rs | 2 +- .../src/methods/unnecessary_map_or.rs | 35 ++-- tests/ui/unnecessary_map_or.stderr | 156 +++++++++++++++--- 3 files changed, 147 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f66466896e5b..55c1e55a3001 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5015,7 +5015,7 @@ impl Methods { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); option_map_or_err_ok::check(cx, expr, recv, def, map); - unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv); + unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv); }, ("map_or_else", [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index b7dbebe60a42..210e7f77930d 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -1,9 +1,8 @@ use std::borrow::Cow; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; @@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Span, sym}; use super::UNNECESSARY_MAP_OR; @@ -42,6 +41,7 @@ pub(super) fn check<'a>( recv: &Expr<'_>, def: &Expr<'_>, map: &Expr<'_>, + method_span: Span, msrv: &Msrv, ) { let ExprKind::Lit(def_kind) = def.kind else { @@ -60,6 +60,8 @@ pub(super) fn check<'a>( Some(_) | None => return, }; + let ext_def_span = def.span.until(map.span); + let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind && let closure_body = cx.tcx.hir().body(map_closure.body) && let closure_body_value = closure_body.value.peel_blocks() @@ -114,26 +116,17 @@ pub(super) fn check<'a>( } .into_string(); - (sugg, "a standard comparison", app) - } else if !def_bool - && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) - && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) - && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) - { + (vec![(expr.span, sugg)], "a standard comparison", app) + } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) { let suggested_name = variant.method_name(); ( - format!("{recv_callsite}.{suggested_name}({span_callsite})",), + vec![(method_span, suggested_name.into()), (ext_def_span, String::default())], suggested_name, Applicability::MachineApplicable, ) - } else if def_bool - && matches!(variant, Variant::Some) - && msrv.meets(msrvs::IS_NONE_OR) - && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) - && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) - { + } else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) { ( - format!("{recv_callsite}.is_none_or({span_callsite})"), + vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())], "is_none_or", Applicability::MachineApplicable, ) @@ -145,13 +138,13 @@ pub(super) fn check<'a>( return; } - span_lint_and_sugg( + span_lint_and_then( cx, UNNECESSARY_MAP_OR, expr.span, "this `map_or` can be simplified", - format!("use {method} instead"), - sugg, - applicability, + |diag| { + diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability); + }, ); } diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr index 2b78996d5f3e..baa490871e30 100644 --- a/tests/ui/unnecessary_map_or.stderr +++ b/tests/ui/unnecessary_map_or.stderr @@ -2,16 +2,25 @@ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` +help: use a standard comparison instead + | +LL | let _ = Some(5) == Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = Some(5) != Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:15:13 @@ -21,7 +30,12 @@ LL | let _ = Some(5).map_or(false, |n| { LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` + | |______^ + | +help: use a standard comparison instead + | +LL | let _ = Some(5) == Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:19:13 @@ -35,113 +49,207 @@ LL | | }); | help: use is_some_and instead | -LL ~ let _ = Some(5).is_some_and(|n| { -LL + let _ = n; -LL + 6 >= 5 -LL ~ }); +LL - let _ = Some(5).map_or(false, |n| { +LL + let _ = Some(5).is_some_and(|n| { | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(vec![5]).map_or(false, |n| n == [5]); +LL + let _ = Some(vec![5]).is_some_and(|n| n == [5]); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); +LL + let _ = Some(vec![1]).is_some_and(|n| vec![2] == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, |n| n == n); +LL + let _ = Some(5).is_some_and(|n| n == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); +LL + let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::, i32>(vec![5]).is_ok_and(|n| n == [5])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); +LL + let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::(5) == Ok(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = Ok::(5) == Ok(5); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)).then(|| 1); + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, |n| n == 5); +LL + let _ = Some(5).is_none_or(|n| n == 5); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, |n| 5 == n); +LL + let _ = Some(5).is_none_or(|n| 5 == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:32:14 | LL | let _ = !Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = !(Some(5) == Some(5)); + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:33:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) || false; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)) || false; + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:34:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)) as usize; + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = r.map_or(false, |x| x == 7); +LL + let _ = r.is_ok_and(|x| x == 7); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); - | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = r.map_or(false, func); +LL + let _ = r.is_ok_and(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, func); +LL + let _ = Some(5).is_some_and(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, func); +LL + let _ = Some(5).is_none_or(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = r == Ok(8); + | ~~~~~~~~~~ error: aborting due to 21 previous errors From 27592e3ec828caa224112bb1e08455ca461687f9 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 18 Jan 2025 14:59:32 +0100 Subject: [PATCH 056/342] Make `unnecessary_map_or` work with ref and `Deref` Receivers which are references to `Option` and `Result`, or who implement `Deref` to one of those types, will be linted as well. --- .../src/methods/unnecessary_map_or.rs | 2 +- tests/ui/unnecessary_map_or.fixed | 17 +++++++++ tests/ui/unnecessary_map_or.rs | 17 +++++++++ tests/ui/unnecessary_map_or.stderr | 38 ++++++++++++++++++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_map_or.rs b/clippy_lints/src/methods/unnecessary_map_or.rs index 210e7f77930d..6dea1506d0e3 100644 --- a/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/clippy_lints/src/methods/unnecessary_map_or.rs @@ -48,7 +48,7 @@ pub(super) fn check<'a>( return; }; - let recv_ty = cx.typeck_results().expr_ty(recv); + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let Bool(def_bool) = def_kind.node else { return; diff --git a/tests/ui/unnecessary_map_or.fixed b/tests/ui/unnecessary_map_or.fixed index efea28e7045c..5a6e77a06b8f 100644 --- a/tests/ui/unnecessary_map_or.fixed +++ b/tests/ui/unnecessary_map_or.fixed @@ -82,3 +82,20 @@ fn msrv_1_81() { // is_none_or added in 1.82.0 let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 }); } + +fn with_refs(o: &mut Option) -> bool { + o.is_none_or(|n| n > 5) || (o as &Option).is_none_or(|n| n < 5) +} + +struct S; + +impl std::ops::Deref for S { + type Target = Option; + fn deref(&self) -> &Self::Target { + &Some(0) + } +} + +fn with_deref(o: &S) -> bool { + o.is_none_or(|n| n > 5) +} diff --git a/tests/ui/unnecessary_map_or.rs b/tests/ui/unnecessary_map_or.rs index 05a0ca816ef6..5ba63121659f 100644 --- a/tests/ui/unnecessary_map_or.rs +++ b/tests/ui/unnecessary_map_or.rs @@ -85,3 +85,20 @@ fn msrv_1_81() { // is_none_or added in 1.82.0 let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 }); } + +fn with_refs(o: &mut Option) -> bool { + o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +} + +struct S; + +impl std::ops::Deref for S { + type Target = Option; + fn deref(&self) -> &Self::Target { + &Some(0) + } +} + +fn with_deref(o: &S) -> bool { + o.map_or(true, |n| n > 5) +} diff --git a/tests/ui/unnecessary_map_or.stderr b/tests/ui/unnecessary_map_or.stderr index baa490871e30..2ae327f0bf88 100644 --- a/tests/ui/unnecessary_map_or.stderr +++ b/tests/ui/unnecessary_map_or.stderr @@ -251,5 +251,41 @@ help: use a standard comparison instead LL | let _ = r == Ok(8); | ~~~~~~~~~~ -error: aborting due to 21 previous errors +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:90:5 + | +LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +LL + o.is_none_or(|n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:90:34 + | +LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +LL + o.map_or(true, |n| n > 5) || (o as &Option).is_none_or(|n| n < 5) + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:103:5 + | +LL | o.map_or(true, |n| n > 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) +LL + o.is_none_or(|n| n > 5) + | + +error: aborting due to 24 previous errors From 8b25ab0c5a7e48cc3fd10d39a56d382a04528958 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 19 Jan 2025 12:47:19 +0200 Subject: [PATCH 057/342] Fix missing upmapping in trait impls completion --- .../src/completions/item_list/trait_impl.rs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 6d1945c45341..246b12526698 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name( acc, ctx, ImplCompletionKind::All, - match name_ref { - Some(name) => name.syntax().text_range(), + match name_ref + .as_ref() + .and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax())) + { + Some(name) => name.text_range(), None => ctx.source_range(), }, impl_, @@ -516,7 +519,7 @@ fn function_declaration( mod tests { use expect_test::expect; - use crate::tests::{check_edit, check_no_kw}; + use crate::tests::{check, check_edit, check_no_kw}; #[test] fn no_completion_inside_fn() { @@ -1639,4 +1642,31 @@ impl DesugaredAsyncTrait for () { "#, ); } + + #[test] + fn within_attr_macro() { + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + f$0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + md proc_macros + kw crate:: + kw self:: + "#]], + ); + } } From 51b0107d287bf1f0e8932c73df0deed1cecfe64c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 18 Sep 2024 14:25:33 +0200 Subject: [PATCH 058/342] New lint: `unnecessary_semicolon` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/unnecessary_semicolon.rs | 63 +++++++++++++++++++++++ tests/ui/unnecessary_semicolon.fixed | 32 ++++++++++++ tests/ui/unnecessary_semicolon.rs | 32 ++++++++++++ tests/ui/unnecessary_semicolon.stderr | 17 ++++++ 7 files changed, 148 insertions(+) create mode 100644 clippy_lints/src/unnecessary_semicolon.rs create mode 100644 tests/ui/unnecessary_semicolon.fixed create mode 100644 tests/ui/unnecessary_semicolon.rs create mode 100644 tests/ui/unnecessary_semicolon.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ceb08eeac3ff..99bac364c47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6173,6 +6173,7 @@ Released 2018-09-13 [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports +[`unnecessary_semicolon`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5e6cfd94283c..251d3bce1060 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -756,6 +756,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a0eae8f6d1cb..7888119567b9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -372,6 +372,7 @@ mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; +mod unnecessary_semicolon; mod unnecessary_struct_initialization; mod unnecessary_wraps; mod unneeded_struct_pattern; @@ -972,5 +973,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); + store.register_late_pass(|_| Box::new(unnecessary_semicolon::UnnecessarySemicolon)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs new file mode 100644 index 000000000000..6bc56dffc57a --- /dev/null +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, MatchSource, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the presence of a semicolon at the end of + /// a `match` or `if` statement evaluating to `()`. + /// + /// ### Why is this bad? + /// The semicolon is not needed, and may be removed to + /// avoid confusion and visual clutter. + /// + /// ### Example + /// ```no_run + /// # let a: u32 = 42; + /// if a > 10 { + /// println!("a is greater than 10"); + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// # let a: u32 = 42; + /// if a > 10 { + /// println!("a is greater than 10"); + /// } + /// ``` + #[clippy::version = "1.86.0"] + pub UNNECESSARY_SEMICOLON, + pedantic, + "unnecessary semicolon after expression returning `()`" +} + +declare_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); + +impl LateLintPass<'_> for UnnecessarySemicolon { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + // rustfmt already takes care of removing semicolons at the end + // of loops. + if let StmtKind::Semi(expr) = stmt.kind + && !stmt.span.from_expansion() + && !expr.span.from_expansion() + && matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(_, _, MatchSource::Normal | MatchSource::Postfix) + ) + && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit + { + let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi()); + span_lint_and_sugg( + cx, + UNNECESSARY_SEMICOLON, + semi_span, + "unnecessary semicolon", + "remove", + String::new(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/tests/ui/unnecessary_semicolon.fixed b/tests/ui/unnecessary_semicolon.fixed new file mode 100644 index 000000000000..36d5c7806fe8 --- /dev/null +++ b/tests/ui/unnecessary_semicolon.fixed @@ -0,0 +1,32 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} diff --git a/tests/ui/unnecessary_semicolon.rs b/tests/ui/unnecessary_semicolon.rs new file mode 100644 index 000000000000..b6fa4f1c9cec --- /dev/null +++ b/tests/ui/unnecessary_semicolon.rs @@ -0,0 +1,32 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + }; + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + }; + //~^ ERROR: unnecessary semicolon +} diff --git a/tests/ui/unnecessary_semicolon.stderr b/tests/ui/unnecessary_semicolon.stderr new file mode 100644 index 000000000000..e6bf36e81e88 --- /dev/null +++ b/tests/ui/unnecessary_semicolon.stderr @@ -0,0 +1,17 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:24:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:30:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 2 previous errors + From 3a7f50f6d3e5f96dc130296c1a94077fb0f37bf8 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 19 Jan 2025 14:48:27 +0100 Subject: [PATCH 059/342] Apply `unnecessary_semicolon` to Clippy sources --- clippy_dev/src/setup/intellij.rs | 2 +- clippy_dev/src/update_lints.rs | 2 +- clippy_lints/src/assigning_clones.rs | 2 +- clippy_lints/src/attrs/mixed_attributes_style.rs | 2 +- clippy_lints/src/casts/cast_possible_wrap.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 4 ++-- clippy_lints/src/checked_conversions.rs | 2 +- clippy_lints/src/default_constructed_unit_structs.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/extra_unused_type_parameters.rs | 2 +- clippy_lints/src/functions/impl_trait_in_params.rs | 2 +- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/implicit_saturating_add.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- clippy_lints/src/iter_over_hash_type.rs | 2 +- clippy_lints/src/let_with_type_underscore.rs | 2 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/literal_representation.rs | 2 +- clippy_lints/src/manual_let_else.rs | 4 ++-- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/matches/manual_filter.rs | 8 ++++---- clippy_lints/src/matches/manual_utils.rs | 2 +- clippy_lints/src/matches/match_like_matches.rs | 2 +- clippy_lints/src/matches/match_wild_enum.rs | 2 +- clippy_lints/src/matches/try_err.rs | 2 +- clippy_lints/src/matches/wild_in_or_pats.rs | 2 +- clippy_lints/src/methods/bytecount.rs | 2 +- clippy_lints/src/methods/bytes_count_to_len.rs | 2 +- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/cloned_instead_of_copied.rs | 2 +- clippy_lints/src/methods/err_expect.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 2 +- clippy_lints/src/methods/iter_with_drain.rs | 2 +- clippy_lints/src/methods/path_ends_with_ext.rs | 2 +- clippy_lints/src/methods/unnecessary_iter_cloned.rs | 2 +- clippy_lints/src/misc.rs | 4 ++-- clippy_lints/src/mismatching_type_param_order.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/multi_assignments.rs | 4 ++-- clippy_lints/src/multiple_unsafe_ops_per_block.rs | 2 +- clippy_lints/src/mutex_atomic.rs | 2 +- clippy_lints/src/needless_late_init.rs | 2 +- clippy_lints/src/non_octal_unix_permissions.rs | 2 +- clippy_lints/src/operators/arithmetic_side_effects.rs | 4 ++-- clippy_lints/src/operators/cmp_owned.rs | 2 +- clippy_lints/src/operators/const_comparisons.rs | 2 +- clippy_lints/src/operators/double_comparison.rs | 2 +- clippy_lints/src/operators/float_cmp.rs | 2 +- clippy_lints/src/operators/modulo_arithmetic.rs | 2 +- clippy_lints/src/operators/modulo_one.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 2 +- clippy_lints/src/redundant_closure_call.rs | 2 +- clippy_lints/src/redundant_type_annotations.rs | 2 +- clippy_lints/src/size_of_in_element_count.rs | 6 +++--- clippy_lints/src/slow_vector_initialization.rs | 2 +- clippy_lints/src/swap.rs | 2 +- clippy_lints/src/transmute/transmute_int_to_non_zero.rs | 2 +- clippy_lints/src/tuple_array_conversions.rs | 2 +- clippy_lints/src/types/borrowed_box.rs | 2 +- clippy_lints/src/unused_io_amount.rs | 2 +- .../src/utils/internal_lints/slow_symbol_comparisons.rs | 2 +- clippy_lints/src/vec.rs | 2 +- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/lib.rs | 4 ++-- clippy_utils/src/macros.rs | 2 +- clippy_utils/src/numeric_literal.rs | 2 +- clippy_utils/src/ty/mod.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- lintcheck/src/config.rs | 2 +- lintcheck/src/main.rs | 4 ++-- src/driver.rs | 2 +- tests/missing-test-files.rs | 2 +- tests/versioncheck.rs | 2 +- 76 files changed, 88 insertions(+), 88 deletions(-) diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index a7138f36a4ef..c56811ee0a01 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -62,7 +62,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result { eprintln!("error: unable to get the absolute path of rustc ({err})"); return Err(()); }, - }; + } } let path = path.join("compiler"); diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 612d1c0ae139..fc0780f89a7f 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -842,7 +842,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, Err(e) => panic_file(e, new_name, "create"), - }; + } match fs::rename(old_name, new_name) { Ok(()) => true, Err(e) => { diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index c8dd77d9578d..c01155ca86e0 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -257,7 +257,7 @@ fn build_sugg<'tcx>( // The receiver may have been a value type, so we need to add an `&` to // be sure the argument to clone_from will be a reference. arg_sugg = arg_sugg.addr(); - }; + } format!("{receiver_sugg}.clone_from({arg_sugg})") }, diff --git a/clippy_lints/src/attrs/mixed_attributes_style.rs b/clippy_lints/src/attrs/mixed_attributes_style.rs index 8c91c65eaf76..3e4bcfbfc190 100644 --- a/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -60,7 +60,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) } outer_attr_kind.insert(kind); }, - }; + } } } diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 3cf4a43b0d4c..504d0a267e47 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -84,6 +84,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca diag .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); - }; + } }); } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 4be53ace6871..45045e58ac75 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -205,7 +205,7 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { // - uncertain if there are any uncertain values (because they could be negative or positive), Sign::Uncertain => return Sign::Uncertain, Sign::ZeroOrPositive => (), - }; + } } // A mul/div is: @@ -236,7 +236,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { // - uncertain if there are any uncertain values (because they could be negative or positive), Sign::Uncertain => return Sign::Uncertain, Sign::ZeroOrPositive => positive_count += 1, - }; + } } // A sum is: diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 364f5c7dc7a0..1edfde974227 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -273,7 +273,7 @@ fn get_types_from_cast<'a>( }, _ => {}, } - }; + } None } diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index 33a97222b8f8..bbd5dc15542d 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -80,6 +80,6 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { String::new(), Applicability::MachineApplicable, ); - }; + } } } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 653726872c64..df9440b2fe88 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { deref_count += 1; }, None => break None, - }; + } }; let use_node = use_cx.use_node(cx); diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index e9e9d00907ea..a090a987d4fc 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { var.span, "C-like enum variant discriminant is not portable to 32-bit targets", ); - }; + } } } } diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index d0159ab89e10..1286b4062ca4 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -183,7 +183,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { .collect() }; self.emit_sugg(spans, msg, help); - }; + } } } diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index 05e341e06fde..752dbc0db4db 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -45,7 +45,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: for param in generics.params { if param.is_impl_trait() { report(cx, param, generics); - }; + } } } } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 1a01f5f885c3..90d3db2700fa 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -160,7 +160,7 @@ fn check_needless_must_use( && !is_must_use_ty(cx, future_ty) { return; - }; + } } span_lint_and_help( diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index dd5908553e59..41d2b18803d9 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -120,7 +120,7 @@ fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, B let ecx = ConstEvalCtxt::new(cx); if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); - }; + } if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 37481dc7feb7..152d506a7c00 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -350,7 +350,7 @@ fn check_with_condition<'tcx>( if cx.typeck_results().expr_ty(cond_left).is_signed() { } else { print_lint_and_sugg(cx, var_name, expr); - }; + } } }, ExprKind::Path(QPath::TypeRelative(_, name)) => { diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index 5131f5b7269b..b1cb6da9475b 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -65,6 +65,6 @@ impl LateLintPass<'_> for IterOverHashType { expr.span, "iteration over unordered hash-based type", ); - }; + } } } diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 5a11702d7ce5..bc124ac779af 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -41,6 +41,6 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { Some(ty.span.with_lo(local.pat.span.hi())), "remove the explicit type `_` declaration", ); - }; + } } } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 540d8b26f8e5..dd641efda7d5 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -784,7 +784,7 @@ fn report_elidable_lifetimes( |diag| { if !include_suggestions { return; - }; + } if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index e2dcb20f906d..a4cedf3bed35 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -251,7 +251,7 @@ impl LiteralDigitGrouping { ); if !consistent { return Err(WarningType::InconsistentDigitGrouping); - }; + } } Ok(()) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index a70955a7c78d..8503dde3fb6b 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -106,7 +106,7 @@ impl<'tcx> QuestionMark { emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body); }, } - }; + } } } @@ -295,7 +295,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) ) { return; - }; + } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 5e58054a9866..78fb7db18c2e 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { } }, _ => return, - }; + } let mut app = Applicability::MachineApplicable; let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0; diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 79de41db3438..d69384a2cb70 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { return; - }; + } if let Res::Local(hir_id) = target_res && let Some(used_mutably) = mutated_variables(then, cx) diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index cfa054706d6b..4cc43e427ec6 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -34,7 +34,7 @@ fn get_cond_expr<'tcx>( needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the * cond */ }); - }; + } None } @@ -45,7 +45,7 @@ fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> if block.stmts.is_empty() { return block.expr; } - }; + } None } @@ -68,14 +68,14 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) && path_to_local_id(arg, target); } - }; + } false } fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); - }; + } false } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index bac5cf88cfbf..0b57740064c1 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -109,7 +109,7 @@ where } }, None => return None, - }; + } let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 223d0dc76569..d697f427c705 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -117,7 +117,7 @@ where if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; } - }; + } span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 91e40e4275c0..595655600890 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -170,7 +170,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ); }); }, - }; + } } enum CommonPrefixSearcher<'a> { diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 6c02207af49d..ff7769af1df4 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine err_ty = ty; } else { return; - }; + } span_lint_and_then( cx, diff --git a/clippy_lints/src/matches/wild_in_or_pats.rs b/clippy_lints/src/matches/wild_in_or_pats.rs index 390ba889fd2e..b75d1ab9a7aa 100644 --- a/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/clippy_lints/src/matches/wild_in_or_pats.rs @@ -13,7 +13,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { && has_non_exhaustive_attr(cx.tcx, *adt_def) { return; - }; + } for arm in arms { if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern diff --git a/clippy_lints/src/methods/bytecount.rs b/clippy_lints/src/methods/bytecount.rs index 4a2124c74a88..687272e550bb 100644 --- a/clippy_lints/src/methods/bytecount.rs +++ b/clippy_lints/src/methods/bytecount.rs @@ -62,5 +62,5 @@ pub(super) fn check<'tcx>( ), applicability, ); - }; + } } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index 34159f2d150e..a9f6a41c2357 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -32,5 +32,5 @@ pub(super) fn check<'tcx>( ), applicability, ); - }; + } } diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index a82abc79f2a2..de22514c37c6 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E format!("{receiver}.as_bytes().get({n}).copied()"), applicability, ); - }; + } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index 2a0a9d3710dc..223a960b800e 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, // &T where T: Copy ty::Ref(_, ty, _) if is_copy(cx, *ty) => {}, _ => return, - }; + } span_lint_and_sugg( cx, CLONED_INSTEAD_OF_COPIED, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 44b55570eead..f2786efa44cb 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -37,7 +37,7 @@ pub(super) fn check( "expect_err".to_string(), Applicability::MachineApplicable, ); - }; + } } /// Given a `Result` type, return its data (`T`). diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 6dc48c26ba93..daa6e0e7f940 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>( if ty.is_str() && can_be_static_str(cx, arg) { return false; } - }; + } true } diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index 163058713377..aa45969c8982 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span "into_iter()".to_string(), Applicability::MaybeIncorrect, ); - }; + } } diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs index febd7fd5cf2f..b3811a335e1a 100644 --- a/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -37,7 +37,7 @@ pub(super) fn check( let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); } else { let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); - }; + } span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 671c189a98e6..c0e015685881 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -87,7 +87,7 @@ pub fn check_for_loop_iter( // skip lint return true; } - }; + } // the lint should not be executed if no violation happens let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b856c929cf67..74280ac3a39c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { ); }, ); - }; + } if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Binary(ref binop, a, b) = expr.kind && (binop.node == BinOpKind::And || binop.node == BinOpKind::Or) @@ -236,7 +236,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { ); }, ); - }; + } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 748289454bea..d52fe7e7d5b9 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { }) => impl_params.push((path.segments[0].ident.to_string(), path.span)), GenericArg::Type(_) => return, _ => (), - }; + } } // find the type that the Impl is for diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 29dcbaa9e62a..06e92985e664 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Impl { .. } | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span), - }; + } let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 05aa425de9ed..bba1b63be277 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Impl { .. } | hir::ItemKind::Use(..) => {}, - }; + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { diff --git a/clippy_lints/src/multi_assignments.rs b/clippy_lints/src/multi_assignments.rs index 9a6b1dfc52b5..4383f28717dc 100644 --- a/clippy_lints/src/multi_assignments.rs +++ b/clippy_lints/src/multi_assignments.rs @@ -56,10 +56,10 @@ impl EarlyLintPass for MultiAssignments { if let ExprKind::Assign(target, source, _) = &expr.kind { if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind { span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); - }; + } if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind { span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); } - }; + } } } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 79252bba74d7..aad6ae52a6db 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -171,7 +171,7 @@ fn collect_unsafe_exprs<'tcx>( }, _ => {}, - }; + } Continue::<(), _>(Descend::Yes) }); diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 388414964588..86c084423b71 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - }; + } } } } diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a67addea9486..954efbcace0a 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -336,7 +336,7 @@ fn check<'tcx>( ); }, _ => {}, - }; + } Some(()) } diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 0caa19cd8443..852c3885f568 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { } }, _ => {}, - }; + } } } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 0eca788c7874..03f732b2516d 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -104,7 +104,7 @@ impl ArithmeticSideEffects { if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; - }; + } let int_type = substs.type_at(0); let unsigned_int_types = [ @@ -214,7 +214,7 @@ impl ArithmeticSideEffects { | hir::BinOpKind::Sub ) { return; - }; + } let (mut actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (mut actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); actual_lhs = expr_or_init(cx, actual_lhs); diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index b0d872e98fd4..cf6b8992973a 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -104,7 +104,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) } else { expr_snip = arg_snip.to_string(); eq_impl = without_deref; - }; + } let span; let hint; diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index 1a0bfd8b9970..10455d3b93a0 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( None, note, ); - }; + } } } diff --git a/clippy_lints/src/operators/double_comparison.rs b/clippy_lints/src/operators/double_comparison.rs index d72a2fc3b1ab..54f50f11e034 100644 --- a/clippy_lints/src/operators/double_comparison.rs +++ b/clippy_lints/src/operators/double_comparison.rs @@ -49,5 +49,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr lint_double_comparison!(==); }, _ => (), - }; + } } diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 8272d3643d42..01dc6a27c33e 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -120,7 +120,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ty::Array(arr_ty, _) = value { return matches!(arr_ty.kind(), ty::Float(_)); - }; + } matches!(value, ty::Float(_)) } diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index c83bdda347a6..691d7b904eff 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( } else { check_non_const_operands(cx, e, lhs); } - }; + } } fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/operators/modulo_one.rs b/clippy_lints/src/operators/modulo_one.rs index 54eea14833ff..fc5565e821ed 100644 --- a/clippy_lints/src/operators/modulo_one.rs +++ b/clippy_lints/src/operators/modulo_one.rs @@ -21,6 +21,6 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: "any number modulo -1 will panic/overflow or result in 0", ); } - }; + } } } diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 794bef7b3214..55676522419c 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -53,6 +53,6 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { ); } } - }; + } } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 41a44de536b1..b4dadef57a3c 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // avoid clippy::double_parens if !is_in_fn_call_arg { hint = hint.maybe_par(); - }; + } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); } diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs index 81556f396141..7bd4d6e993b4 100644 --- a/clippy_lints/src/redundant_type_annotations.rs +++ b/clippy_lints/src/redundant_type_annotations.rs @@ -215,6 +215,6 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { }, _ => (), } - }; + } } } diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index db1c75fc3dea..f72ff10dd43c 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -97,7 +97,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( && let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next() { return Some((pointee_ty, count)); - }; + } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods && let method_ident = method_path.ident.as_str() @@ -108,7 +108,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx.typeck_results().expr_ty(ptr_self).kind() { return Some((*pointee_ty, count)); - }; + } None } @@ -130,6 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { && pointee_ty == ty_used_for_size_of { span_lint_and_help(cx, SIZE_OF_IN_ELEMENT_COUNT, count_expr.span, LINT_MSG, None, HELP_MSG); - }; + } } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index cdf538fce5c7..d26288adb391 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -191,7 +191,7 @@ impl SlowVectorInit { InitializationType::Extend(e) | InitializationType::Resize(e) => { Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization"); }, - }; + } } fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &'static str) { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index ff1168005123..9b4c3d275ae7 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -296,7 +296,7 @@ fn check_xor_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { { let span = s1.span.to(s3.span); generate_swap_warning(block, cx, lhs0, rhs0, rhs1, rhs2, span, true); - }; + } } } diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 3729dfd3e86f..f27aaa2fa77a 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; - }; + } let int_ty = substs.type_at(0); if from_ty != int_ty { diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 99a55f9fc357..008e09dd8bd1 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -119,7 +119,7 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & && let LitKind::Int(val, _) = lit.node { return (val == i as u128).then_some(lhs); - }; + } None }) diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index bde88ab61adf..a1d0f1845de2 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m Applicability::Unspecified, ); return true; - }; + } false }, _ => false, diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 7c9455bf8abe..e65123b8a949 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -182,7 +182,7 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { emit_lint(cx, expr.span, expr.hir_id, op, &[]); }, _ => {}, - }; + } } fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option { diff --git a/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs index 49aad881994e..b8bcb9b37560 100644 --- a/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs +++ b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs @@ -69,6 +69,6 @@ impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { ), applicability, ); - }; + } } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 0730b561bc29..03c667846b61 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { }; if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { return; - }; + } // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` let callsite = expr.span.parent_callsite().unwrap_or(expr.span); diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 60be7e4a4d39..6bb876322f24 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -475,7 +475,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; - }; + } }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a0de9a76a0ef..ab4a6d0d0a28 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -815,7 +815,7 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &' e = ep; }, _ => break e, - }; + } }; result.reverse(); (result, root) @@ -2045,7 +2045,7 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t { return Some(expr); } - }; + } None } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 45beb146eb6a..f4c730ef118b 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -251,7 +251,7 @@ impl<'a> PanicExpn<'a> { // This has no argument if name == "panic_cold_explicit" { return Some(Self::Empty); - }; + } let [arg, rest @ ..] = args else { return None; diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 2c49df9d807f..bb2a62821100 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -125,7 +125,7 @@ impl<'a> NumericLiteral<'a> { integer = &digits[..exp_start]; } else { fraction = Some(&digits[integer.len() + 1..exp_start]); - }; + } exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 802560a80157..f2bbdda70585 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -118,7 +118,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { return true; } - }; + } }, _ => (), } diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index d7640ebfb006..5bf7ce99c030 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -55,7 +55,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { && let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id)) { receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id); - }; + } let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false); let rhs = if type_is_inferable_from_arguments(cx, expr) { meet( diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index bd4fcc5e337e..af243f94274d 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -107,7 +107,7 @@ impl LintcheckConfig { } else { std::thread::available_parallelism().map_or(1, NonZero::get) }; - }; + } for lint_name in &mut config.lint_filter { *lint_name = format!( diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 03e2a24f6f98..e88d9f427bec 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -145,7 +145,7 @@ impl Crate { assert_eq!(status.code(), Some(0)); return Vec::new(); - }; + } if !config.fix { cmd.arg("--message-format=json"); @@ -313,7 +313,7 @@ fn lintcheck(config: LintcheckConfig) { filter }) .collect_into(&mut lint_level_args); - }; + } let crates: Vec = crates .into_iter() diff --git a/src/driver.rs b/src/driver.rs index 75ef60a5dc8a..9cdb39db3f53 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -223,7 +223,7 @@ pub fn main() { if !has_sysroot_arg(args) { args.extend(vec!["--sysroot".into(), sys_root]); } - }; + } }; // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 64eba5e0888a..565dcd73f582 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -60,7 +60,7 @@ fn explore_directory(dir: &Path) -> Vec { } }, _ => {}, - }; + } } } } diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index e29898f068d3..ed357137095b 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -88,5 +88,5 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { _ => { panic!("Failed to parse rustc version: {vsplit:?}"); }, - }; + } } From 1ccef58dc0b09c02934cfb3463a7fa1432470008 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 19 Jan 2025 20:22:09 +0100 Subject: [PATCH 060/342] `useless_conversion`: add needed adjustments to suggestion --- clippy_lints/src/useless_conversion.rs | 22 ++++++++- tests/ui/useless_conversion.fixed | 53 ++++++++++++++++++++++ tests/ui/useless_conversion.rs | 53 ++++++++++++++++++++++ tests/ui/useless_conversion.stderr | 62 +++++++++++++++++++++++++- 4 files changed, 187 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 7ffab81a5444..26fde5224a42 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -12,6 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -251,6 +252,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // ^^^ let (into_iter_recv, depth) = into_iter_deep_call(cx, into_iter_recv); + // The receiver may not implement `IntoIterator`, it may have been + // auto-dereferenced. + let adjustments = adjustments(cx, into_iter_recv); + let plural = if depth == 0 { "" } else { "s" }; let mut applicability = Applicability::MachineApplicable; let sugg = snippet_with_applicability( @@ -258,8 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { into_iter_recv.span.source_callsite(), "", &mut applicability, - ) - .into_owned(); + ); + let sugg = format!("{adjustments}{sugg}"); span_lint_and_then( cx, USELESS_CONVERSION, @@ -431,3 +436,16 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) } false } + +fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { + let mut prefix = String::new(); + for adj in cx.typeck_results().expr_adjustments(expr) { + match adj.kind { + Adjust::Deref(_) => prefix = format!("*{prefix}"), + Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })) => prefix = format!("&mut {prefix}"), + Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)) => prefix = format!("&{prefix}"), + _ => {}, + } + } + prefix +} diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 2f7edd92bb7c..697d437b3885 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -342,3 +342,56 @@ fn gen_identity(x: [T; 3]) -> Vec { x.into_iter().collect() //~^ useless_conversion } + +mod issue11819 { + fn takes_into_iter(_: impl IntoIterator) {} + + pub struct MyStruct { + my_field: T, + } + + impl MyStruct { + pub fn with_ref<'a>(&'a mut self) + where + &'a T: IntoIterator, + { + takes_into_iter(&self.my_field); + //~^ useless_conversion + } + + pub fn with_ref_mut<'a>(&'a mut self) + where + &'a mut T: IntoIterator, + { + takes_into_iter(&mut self.my_field); + //~^ useless_conversion + } + + pub fn with_deref(&mut self) + where + T: std::ops::Deref, + Y: IntoIterator + Copy, + { + takes_into_iter(*self.my_field); + //~^ useless_conversion + } + + pub fn with_reborrow<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref, + &'a Y: IntoIterator, + { + takes_into_iter(&*self.my_field); + //~^ useless_conversion + } + + pub fn with_reborrow_mut<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref + std::ops::DerefMut, + &'a mut Y: IntoIterator, + { + takes_into_iter(&mut *self.my_field); + //~^ useless_conversion + } + } +} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index eacdf77f9052..4d8ad61a8c99 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -342,3 +342,56 @@ fn gen_identity(x: [T; 3]) -> Vec { x.into_iter().map(Into::into).collect() //~^ useless_conversion } + +mod issue11819 { + fn takes_into_iter(_: impl IntoIterator) {} + + pub struct MyStruct { + my_field: T, + } + + impl MyStruct { + pub fn with_ref<'a>(&'a mut self) + where + &'a T: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_ref_mut<'a>(&'a mut self) + where + &'a mut T: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_deref(&mut self) + where + T: std::ops::Deref, + Y: IntoIterator + Copy, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_reborrow<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref, + &'a Y: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_reborrow_mut<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref + std::ops::DerefMut, + &'a mut Y: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + } +} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 6aeb382902ba..5227a3863d87 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -274,5 +274,65 @@ error: useless conversion to the same type: `T` LL | x.into_iter().map(Into::into).collect() | ^^^^^^^^^^^^^^^^ help: consider removing -error: aborting due to 36 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:358:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&self.my_field` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:366:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&mut self.my_field` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:375:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `*self.my_field` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:384:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&*self.my_field` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:393:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&mut *self.my_field` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 41 previous errors From 2b488c3e51c8cd810db13111ce4a75a774a100b9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 20 Jan 2025 03:29:04 +0000 Subject: [PATCH 061/342] Get rid of mir::Const::from_ty_const --- clippy_lints/src/matches/overlapping_arms.rs | 7 ++----- clippy_utils/src/lib.rs | 9 ++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 4a5d3c516b88..4184f8b9e6e8 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; use rustc_lint::LateContext; -use rustc_middle::mir; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -36,14 +35,12 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) let lhs_const = if let Some(lhs) = lhs { ConstEvalCtxt::new(cx).eval_pat_expr(lhs)? } else { - let min_val_const = ty.numeric_min_val(cx.tcx)?; - mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? + mir_to_const(cx.tcx, ty.numeric_min_val(cx.tcx)?)? }; let rhs_const = if let Some(rhs) = rhs { ConstEvalCtxt::new(cx).eval_pat_expr(rhs)? } else { - let max_val_const = ty.numeric_max_val(cx.tcx)?; - mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? + mir_to_const(cx.tcx, ty.numeric_max_val(cx.tcx)?)? }; let lhs_val = lhs_const.int_value(cx.tcx, ty)?; let rhs_val = rhs_const.int_value(cx.tcx, ty)?; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index eecfc3fb13f8..cf0993e1f4a3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -112,7 +112,6 @@ use rustc_hir::{ use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::mir::Const; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; @@ -1584,8 +1583,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) - && let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) + && let Some(min_const) = bnd_ty.numeric_min_val(cx.tcx) + && let Some(min_const) = mir_to_const(cx.tcx, min_const) && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) { start_const == min_const @@ -1597,8 +1596,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti RangeLimits::Closed => { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) - && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) - && let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) + && let Some(max_const) = bnd_ty.numeric_max_val(cx.tcx) + && let Some(max_const) = mir_to_const(cx.tcx, max_const) && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) { end_const == max_const From 01907f77fc60689bf1c21150fd86f4c6e23c5de3 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 19 Jan 2025 21:01:03 +0100 Subject: [PATCH 062/342] `useless_conversion`: use multipart suggestion to make adjustments more visible --- clippy_lints/src/useless_conversion.rs | 29 +++++------ tests/ui/useless_conversion.stderr | 71 +++++++++++++++++++++----- 2 files changed, 69 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 26fde5224a42..5e452c6d2ac0 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ @@ -252,30 +252,25 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // ^^^ let (into_iter_recv, depth) = into_iter_deep_call(cx, into_iter_recv); - // The receiver may not implement `IntoIterator`, it may have been - // auto-dereferenced. - let adjustments = adjustments(cx, into_iter_recv); - - let plural = if depth == 0 { "" } else { "s" }; - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability( - cx, - into_iter_recv.span.source_callsite(), - "", - &mut applicability, - ); - let sugg = format!("{adjustments}{sugg}"); span_lint_and_then( cx, USELESS_CONVERSION, e.span, "explicit call to `.into_iter()` in function argument accepting `IntoIterator`", |diag| { - diag.span_suggestion( - e.span, + let receiver_span = into_iter_recv.span.source_callsite(); + let adjustments = adjustments(cx, into_iter_recv); + let mut sugg = if adjustments.is_empty() { + vec![] + } else { + vec![(receiver_span.shrink_to_lo(), adjustments)] + }; + let plural = if depth == 0 { "" } else { "s" }; + sugg.push((e.span.with_lo(receiver_span.hi()), String::new())); + diag.multipart_suggestion( format!("consider removing the `.into_iter()`{plural}"), sugg, - applicability, + Applicability::MachineApplicable, ); diag.span_note(span, "this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`"); }, diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 5227a3863d87..ed50f3071862 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -122,7 +122,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:189:7 | LL | b(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -134,7 +136,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:190:7 | LL | c(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:180:18 @@ -146,7 +150,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:191:7 | LL | d(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:183:12 @@ -158,7 +164,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:194:7 | LL | b(vec![1, 2].into_iter().into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` + | ^^^^^^^^^^------------------------ + | | + | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -170,7 +178,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:195:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` + | ^^^^^^^^^^------------------------------------ + | | + | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -182,7 +192,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:241:24 | LL | foo2::([1, 2, 3].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:220:12 @@ -194,7 +206,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:249:14 | LL | foo3([1, 2, 3].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:229:12 @@ -206,7 +220,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:258:16 | LL | S1.foo([1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` + | ^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:255:27 @@ -218,7 +234,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:277:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); - | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` + | ^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:264:20 @@ -278,61 +296,86 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:358:29 | LL | takes_into_iter(self.my_field.into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&self.my_field` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:347:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&self.my_field); + | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` --> tests/ui/useless_conversion.rs:366:29 | LL | takes_into_iter(self.my_field.into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&mut self.my_field` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:347:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&mut self.my_field); + | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` --> tests/ui/useless_conversion.rs:375:29 | LL | takes_into_iter(self.my_field.into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `*self.my_field` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:347:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(*self.my_field); + | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` --> tests/ui/useless_conversion.rs:384:29 | LL | takes_into_iter(self.my_field.into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&*self.my_field` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:347:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&*self.my_field); + | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` --> tests/ui/useless_conversion.rs:393:29 | LL | takes_into_iter(self.my_field.into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `&mut *self.my_field` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:347:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&mut *self.my_field); + | error: aborting due to 41 previous errors From eff57e29a9fa79985350fe89e86bd74f0d065a62 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 20 Jan 2025 10:47:13 +0100 Subject: [PATCH 063/342] Fix out-of-date comment This comment was left behind when the method receiver was split out from the method arguments in 4bcaddeeb23544eb2c86b600c3d775e2773758c2. --- clippy_lints/src/methods/unnecessary_sort_by.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 2732a89e07bd..ebc08503decb 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -44,8 +44,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident)) }, // The two exprs are method calls. - // Check to see that the function is the same and the arguments are mirrored - // This is enough because the receiver of the method is listed in the arguments + // Check to see that the function is the same and the arguments and receivers are mirrored ( ExprKind::MethodCall(left_segment, left_receiver, left_args, _), ExprKind::MethodCall(right_segment, right_receiver, right_args, _), From 48d17b4181e3ddffad73d5af1377af66b2ddf4f3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Jan 2025 16:23:19 +0100 Subject: [PATCH 064/342] Move dual blanket impl logic from source analyzer to goto_def --- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-expand/src/name.rs | 12 +++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 9 +++ .../rust-analyzer/crates/hir/src/semantics.rs | 16 +++- .../crates/hir/src/source_analyzer.rs | 74 ------------------- .../crates/ide-db/src/famous_defs.rs | 12 +++ .../crates/ide/src/goto_definition.rs | 65 +++++++++------- .../crates/intern/src/symbol/symbols.rs | 6 ++ .../crates/test-utils/src/minicore.rs | 10 --- 9 files changed, 92 insertions(+), 113 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 84c105a0a346..da9ffae8aab0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -910,6 +910,7 @@ pub enum AssocItemId { ConstId(ConstId), TypeAliasId(TypeAliasId), } + // FIXME: not every function, ... is actually an assoc item. maybe we should make // sure that you can only turn actual assoc items into AssocItemIds. This would // require not implementing From, and instead having some checked way of diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index cc53d2e34aac..9cfa3b4b32b0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -51,12 +51,24 @@ impl PartialEq for Name { } } +impl PartialEq<&Symbol> for Name { + fn eq(&self, &sym: &&Symbol) -> bool { + self.symbol == *sym + } +} + impl PartialEq for Symbol { fn eq(&self, name: &Name) -> bool { *self == name.symbol } } +impl PartialEq for &Symbol { + fn eq(&self, name: &Name) -> bool { + **self == name.symbol + } +} + /// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct UnescapedName<'a>(&'a Name); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index db3121d3cd35..220baeb15209 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2756,6 +2756,15 @@ impl Trait { traits.iter().map(|tr| Trait::from(*tr)).collect() } + pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq) -> Option { + db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then( + |&(_, it)| match it { + AssocItemId::FunctionId(id) => Some(Function { id }), + _ => None, + }, + ) + } + pub fn items(self, db: &dyn HirDatabase) -> Vec { db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 523bc6f10aab..0b76f2e1121b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } - pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option { - self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call) + /// Env is used to derive the trait environment + // FIXME: better api for the trait environment + pub fn resolve_impl_method( + &self, + env: Type, + trait_: Trait, + func: Function, + subst: impl IntoIterator, + ) -> Option { + let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); + for s in subst { + substs = substs.push(s.ty); + } + Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) } fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 6b78d7a3631f..b699ccde4128 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -322,68 +322,6 @@ impl SourceAnalyzer { } } - // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str. - pub(crate) fn resolve_known_blanket_dual_impls( - &self, - db: &dyn HirDatabase, - call: &ast::MethodCallExpr, - ) -> Option { - // e.g. if the method call is let b = a.into(), - // - receiver_type is A (type of a) - // - return_type is B (type of b) - // We will find the definition of B::from(a: A). - let callable = self.resolve_method_call_as_callable(db, call)?; - let (_, receiver_type) = callable.receiver_param(db)?; - let return_type = callable.return_type(); - let (search_method, substs) = match call.name_ref()?.text().as_str() { - "into" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?; - ( - self.trait_fn(db, trait_, "from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.ty) - .push(receiver_type.ty) - .build(), - ) - } - "try_into" => { - let trait_ = self - .resolver - .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?; - ( - self.trait_fn(db, trait_, "try_from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - // If the method is try_into() or parse(), return_type is Result. - // Get T from type arguments of Result. - .push(return_type.type_arguments().next()?.ty) - .push(receiver_type.ty) - .build(), - ) - } - "parse" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?; - ( - self.trait_fn(db, trait_, "from_str")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.type_arguments().next()?.ty) - .build(), - ) - } - _ => return None, - }; - - let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs); - // If found_method == search_method, the method in trait itself is resolved. - // It means the blanket dual impl is not found. - if found_method == search_method { - None - } else { - Some(found_method.into()) - } - } - pub(crate) fn resolve_expr_as_callable( &self, db: &dyn HirDatabase, @@ -1309,18 +1247,6 @@ impl SourceAnalyzer { Some((trait_id, fn_id)) } - fn trait_fn( - &self, - db: &dyn HirDatabase, - trait_id: TraitId, - method_name: &str, - ) -> Option { - db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item { - AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t), - _ => None, - }) - } - fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 9e3506d6f53b..1dc61e3f0a8d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:cmp:Ord") } + pub fn core_convert_FromStr(&self) -> Option { + self.find_trait("core:str:FromStr") + } + pub fn core_convert_From(&self) -> Option { self.find_trait("core:convert:From") } @@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_TryFrom(&self) -> Option { + self.find_trait("core:convert:TryFrom") + } + + pub fn core_convert_TryInto(&self) -> Option { + self.find_trait("core:convert:TryInto") + } + pub fn core_convert_Index(&self) -> Option { self.find_trait("core:ops:Index") } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f804cc367727..2c6c6e6e6c14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -5,10 +5,14 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{ + sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt, + ModuleDef, Semantics, +}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, + famous_defs::FamousDefs, helpers::pick_best_token, RootDatabase, SymbolKind, }; @@ -129,15 +133,45 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } -// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr. +// If the token is into(), try_into(), search the definition of From, TryFrom. fn find_definition_for_known_blanket_dual_impls( sema: &Semantics<'_, RootDatabase>, original_token: &SyntaxToken, ) -> Option> { let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; - let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?; + let callable = sema.resolve_method_call_as_callable(&method_call)?; + let CallableKind::Function(f) = callable.kind() else { return None }; + let t = f.as_assoc_item(sema.db)?.container_trait(sema.db)?; - let def = Definition::from(target_method); + let return_type = callable.return_type(); + let fd = FamousDefs(sema, return_type.krate(sema.db)); + let fn_name = f.name(sema.db); + let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { + let dual = fd.core_convert_From()?; + let dual_f = dual.function(sema.db, &sym::from)?; + sema.resolve_impl_method( + return_type.clone(), + dual, + dual_f, + [return_type, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { + let dual = fd.core_convert_TryFrom()?; + let dual_f = dual.function(sema.db, &sym::try_from)?; + sema.resolve_impl_method( + return_type.clone(), + dual, + dual_f, + // Extract the `T` from `Result` + [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], + )? + } else { + return None; + }; + // Assert that we got a trait impl function, if we are back in a trait definition we didn't + // succeed + let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; + let def = Definition::from(f); Some(def_to_nav(sema.db, def)) } @@ -3157,29 +3191,6 @@ impl TryInto for A { fn f() { let a = A; let b: Result = a.try_into$0(); -} - "#, - ); - } - - #[test] - fn parse_call_to_from_str_definition() { - check( - r#" -//- minicore: from, str -struct A; - -impl FromStr for A { - type Error = String; - - fn from_str(value: &str) -> Result { - //^^^^^^^^ - Ok(A) - } -} - -fn f() { - let a: Result = "aaaaaa".parse$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index b3b46421b50f..e7c3668c0fc9 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -240,6 +240,7 @@ define_symbols! { format_unsafe_arg, format, freeze, + from, From, FromStr, from_output, @@ -273,6 +274,8 @@ define_symbols! { index_mut, index, Index, + into, + Into, into_future, into_iter, IntoFuture, @@ -361,6 +364,7 @@ define_symbols! { panic_nounwind, panic, Param, + parse, partial_ord, PartialEq, PartialOrd, @@ -459,8 +463,10 @@ define_symbols! { transmute_opts, transmute_trait, transparent, + try_into, Try, TryFrom, + try_from, tuple_trait, u128, u16, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index fd06736a2524..4a44eaf5d111 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1574,15 +1574,6 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } - pub trait FromStr: Sized { - type Err; - fn from_str(s: &str) -> Result; - } - impl str { - pub fn parse(&self) -> Result { - FromStr::from_str(self) - } - } } // endregion:str @@ -1857,7 +1848,6 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result - str::FromStr, // :str }; } From 4bc683dfd2934ed7e9ae055e393f06ecf9115b19 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Jan 2025 16:32:17 +0100 Subject: [PATCH 065/342] Bring back goto def redirect for parse -> FromStr --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 ++ .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- .../crates/ide/src/goto_definition.rs | 46 +++++++++++++++++-- .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/test-utils/src/minicore.rs | 10 ++++ 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 220baeb15209..4938478bb121 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4682,6 +4682,10 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool)) } + pub fn is_str(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Str) + } + pub fn is_never(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::Never) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0b76f2e1121b..708db2c08dda 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1441,7 +1441,7 @@ impl<'db> SemanticsImpl<'db> { /// Env is used to derive the trait environment // FIXME: better api for the trait environment - pub fn resolve_impl_method( + pub fn resolve_trait_impl_method( &self, env: Type, trait_: Trait, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 2c6c6e6e6c14..99d0d6af716d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -141,15 +141,35 @@ fn find_definition_for_known_blanket_dual_impls( let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; let callable = sema.resolve_method_call_as_callable(&method_call)?; let CallableKind::Function(f) = callable.kind() else { return None }; - let t = f.as_assoc_item(sema.db)?.container_trait(sema.db)?; + let assoc = f.as_assoc_item(sema.db)?; let return_type = callable.return_type(); let fd = FamousDefs(sema, return_type.krate(sema.db)); + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(impl_) + if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse => + { + let t = fd.core_convert_FromStr()?; + let t_f = t.function(sema.db, &sym::from_str)?; + return sema + .resolve_trait_impl_method( + return_type.clone(), + t, + t_f, + [return_type.type_arguments().next()?], + ) + .map(|f| def_to_nav(sema.db, f.into())); + } + hir::AssocItemContainer::Impl(_) => return None, + }; + let fn_name = f.name(sema.db); let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { let dual = fd.core_convert_From()?; let dual_f = dual.function(sema.db, &sym::from)?; - sema.resolve_impl_method( + sema.resolve_trait_impl_method( return_type.clone(), dual, dual_f, @@ -158,7 +178,7 @@ fn find_definition_for_known_blanket_dual_impls( } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { let dual = fd.core_convert_TryFrom()?; let dual_f = dual.function(sema.db, &sym::try_from)?; - sema.resolve_impl_method( + sema.resolve_trait_impl_method( return_type.clone(), dual, dual_f, @@ -3191,6 +3211,26 @@ impl TryInto for A { fn f() { let a = A; let b: Result = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn parse_call_to_from_str_definition() { + check( + r#" +//- minicore: from, str +struct A; +impl FromStr for A { + type Error = String; + fn from_str(value: &str) -> Result { + //^^^^^^^^ + Ok(A) + } +} +fn f() { + let a: Result = "aaaaaa".parse$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index e7c3668c0fc9..59c0c9dca1c4 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -243,6 +243,7 @@ define_symbols! { from, From, FromStr, + from_str, from_output, from_residual, from_usize, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 4a44eaf5d111..fd06736a2524 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1574,6 +1574,15 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } + pub trait FromStr: Sized { + type Err; + fn from_str(s: &str) -> Result; + } + impl str { + pub fn parse(&self) -> Result { + FromStr::from_str(self) + } + } } // endregion:str @@ -1848,6 +1857,7 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result + str::FromStr, // :str }; } From e9b410d29ce996abc31bd85f8168936f6eeda2aa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Jan 2025 16:42:34 +0100 Subject: [PATCH 066/342] Goto `Display::fmt` when invoked on `to_string` --- .../rust-analyzer/crates/hir/src/semantics.rs | 2 + .../crates/ide-db/src/famous_defs.rs | 7 ++++ .../crates/ide/src/goto_definition.rs | 39 +++++++++++++++++++ .../crates/intern/src/symbol/symbols.rs | 1 + 4 files changed, 49 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 708db2c08dda..09470bed9cfb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1483,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } + // This does not resolve the method call to the correct trait impl! + // We should probably fix that. pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 1dc61e3f0a8d..7039d83a5d18 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -142,6 +142,13 @@ impl FamousDefs<'_, '_> { self.find_macro("core:unimplemented") } + pub fn core_fmt_Display(&self) -> Option { + self.find_trait("core:fmt:Display") + } + + pub fn alloc_string_ToString(&self) -> Option { + self.find_trait("alloc:string:ToString") + } pub fn builtin_crates(&self) -> impl Iterator { IntoIterator::into_iter([ self.std(), diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 99d0d6af716d..d18732a6b846 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -185,6 +185,15 @@ fn find_definition_for_known_blanket_dual_impls( // Extract the `T` from `Result` [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], )? + } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) { + let dual = fd.core_fmt_Display()?; + let dual_f = dual.function(sema.db, &sym::fmt)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [callable.receiver_param(sema.db)?.1.strip_reference()], + )? } else { return None; }; @@ -3231,6 +3240,36 @@ impl FromStr for A { } fn f() { let a: Result = "aaaaaa".parse$0(); +} + "#, + ); + } + + #[test] + fn to_string_call_to_display_definition() { + check( + r#" +//- minicore:fmt +//- /alloc.rs crate:alloc +pub mod string { + pub struct String; + pub trait ToString { + fn to_string(&self) -> String; + } + + impl ToString for T { + fn to_string(&self) -> String { String } + } +} +//- /lib.rs crate:lib deps:alloc +use alloc::string::ToString; +struct A; +impl core::fmt::Display for A { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {} + // ^^^ +} +fn f() { + A.to_string$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 59c0c9dca1c4..a29ebad31f03 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -460,6 +460,7 @@ define_symbols! { test_case, test, thiscall, + to_string, trace_macros, transmute_opts, transmute_trait, From 656d1cce7ec086a2fcaccd269d1988781224a879 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 11 Jan 2025 12:01:31 +0100 Subject: [PATCH 067/342] alloc: add `#![warn(unreachable_pub)]` --- library/alloc/src/collections/btree/append.rs | 10 +- library/alloc/src/collections/btree/borrow.rs | 10 +- .../collections/btree/dedup_sorted_iter.rs | 4 +- library/alloc/src/collections/btree/fix.rs | 13 +- library/alloc/src/collections/btree/mem.rs | 4 +- .../alloc/src/collections/btree/merge_iter.rs | 8 +- library/alloc/src/collections/btree/mod.rs | 4 +- .../alloc/src/collections/btree/navigate.rs | 72 +++--- library/alloc/src/collections/btree/node.rs | 209 ++++++++++-------- .../alloc/src/collections/btree/node/tests.rs | 4 +- library/alloc/src/collections/btree/remove.rs | 2 +- library/alloc/src/collections/btree/search.rs | 21 +- library/alloc/src/collections/btree/split.rs | 8 +- .../src/collections/linked_list/tests.rs | 2 +- library/alloc/src/lib.rs | 3 +- library/alloc/src/raw_vec.rs | 44 ++-- library/alloc/src/slice.rs | 1 + library/alloc/src/testing/crash_test.rs | 20 +- library/alloc/src/testing/mod.rs | 6 +- library/alloc/src/testing/ord_chaos.rs | 10 +- library/alloc/src/testing/rng.rs | 6 +- 21 files changed, 251 insertions(+), 210 deletions(-) diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index d137d2721ee4..091376d5d685 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -16,7 +16,7 @@ impl Root { /// a `BTreeMap`, both iterators should produce keys in strictly ascending /// order, each greater than all keys in the tree, including any keys /// already in the tree upon entry. - pub fn append_from_sorted_iters( + pub(super) fn append_from_sorted_iters( &mut self, left: I, right: I, @@ -36,8 +36,12 @@ impl Root { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - pub fn bulk_push(&mut self, iter: I, length: &mut usize, alloc: A) - where + pub(super) fn bulk_push( + &mut self, + iter: I, + length: &mut usize, + alloc: A, + ) where I: Iterator, { let mut cur_node = self.borrow_mut().last_leaf_edge().into_node(); diff --git a/library/alloc/src/collections/btree/borrow.rs b/library/alloc/src/collections/btree/borrow.rs index 000b9bd0fab4..e848ac3f2d19 100644 --- a/library/alloc/src/collections/btree/borrow.rs +++ b/library/alloc/src/collections/btree/borrow.rs @@ -11,7 +11,7 @@ use core::ptr::NonNull; /// the compiler to follow. A `DormantMutRef` allows you to check borrowing /// yourself, while still expressing its stacked nature, and encapsulating /// the raw pointer code needed to do this without undefined behavior. -pub struct DormantMutRef<'a, T> { +pub(super) struct DormantMutRef<'a, T> { ptr: NonNull, _marker: PhantomData<&'a mut T>, } @@ -23,7 +23,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// Capture a unique borrow, and immediately reborrow it. For the compiler, /// the lifetime of the new reference is the same as the lifetime of the /// original reference, but you promise to use it for a shorter period. - pub fn new(t: &'a mut T) -> (&'a mut T, Self) { + pub(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { let ptr = NonNull::from(t); // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose // only this reference, so it is unique. @@ -37,7 +37,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken(self) -> &'a mut T { + pub(super) unsafe fn awaken(self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -48,7 +48,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow(&mut self) -> &'a mut T { + pub(super) unsafe fn reborrow(&mut self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -59,7 +59,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow_shared(&self) -> &'a T { + pub(super) unsafe fn reborrow_shared(&self) -> &'a T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &*self.ptr.as_ptr() } } diff --git a/library/alloc/src/collections/btree/dedup_sorted_iter.rs b/library/alloc/src/collections/btree/dedup_sorted_iter.rs index cd6a88f32912..6bcf0bca519a 100644 --- a/library/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/library/alloc/src/collections/btree/dedup_sorted_iter.rs @@ -6,7 +6,7 @@ use core::iter::Peekable; /// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1]. /// /// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter -pub struct DedupSortedIter +pub(super) struct DedupSortedIter where I: Iterator, { @@ -17,7 +17,7 @@ impl DedupSortedIter where I: Iterator, { - pub fn new(iter: I) -> Self { + pub(super) fn new(iter: I) -> Self { Self { iter: iter.peekable() } } } diff --git a/library/alloc/src/collections/btree/fix.rs b/library/alloc/src/collections/btree/fix.rs index 09edea3555ad..b0c675979469 100644 --- a/library/alloc/src/collections/btree/fix.rs +++ b/library/alloc/src/collections/btree/fix.rs @@ -57,7 +57,10 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// /// This method does not expect ancestors to already be underfull upon entry /// and panics if it encounters an empty ancestor. - pub fn fix_node_and_affected_ancestors(mut self, alloc: A) -> bool { + pub(super) fn fix_node_and_affected_ancestors( + mut self, + alloc: A, + ) -> bool { loop { match self.fix_node_through_parent(alloc.clone()) { Ok(Some(parent)) => self = parent.forget_type(), @@ -70,7 +73,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { impl Root { /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. - pub fn fix_top(&mut self, alloc: A) { + pub(super) fn fix_top(&mut self, alloc: A) { while self.height() > 0 && self.len() == 0 { self.pop_internal_level(alloc.clone()); } @@ -79,7 +82,7 @@ impl Root { /// Stocks up or merge away any underfull nodes on the right border of the /// tree. The other nodes, those that are not the root nor a rightmost edge, /// must already have at least MIN_LEN elements. - pub fn fix_right_border(&mut self, alloc: A) { + pub(super) fn fix_right_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().last_kv().fix_right_border_of_right_edge(alloc.clone()); @@ -88,7 +91,7 @@ impl Root { } /// The symmetric clone of `fix_right_border`. - pub fn fix_left_border(&mut self, alloc: A) { + pub(super) fn fix_left_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().first_kv().fix_left_border_of_left_edge(alloc.clone()); @@ -99,7 +102,7 @@ impl Root { /// Stocks up any underfull nodes on the right border of the tree. /// The other nodes, those that are neither the root nor a rightmost edge, /// must be prepared to have up to MIN_LEN elements stolen. - pub fn fix_right_border_of_plentiful(&mut self) { + pub(super) fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if rightmost child is underfull. diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index d738c5c47b4c..4643c4133d55 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -6,7 +6,7 @@ use core::{intrinsics, mem, ptr}; /// If a panic occurs in the `change` closure, the entire process will be aborted. #[allow(dead_code)] // keep as illustration and for future use #[inline] -pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { +pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { replace(v, |value| (change(value), ())) } @@ -15,7 +15,7 @@ pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] -pub fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { +pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs index 7f23d93b990f..c5b93d30a118 100644 --- a/library/alloc/src/collections/btree/merge_iter.rs +++ b/library/alloc/src/collections/btree/merge_iter.rs @@ -4,7 +4,7 @@ use core::iter::FusedIterator; /// Core of an iterator that merges the output of two strictly ascending iterators, /// for instance a union or a symmetric difference. -pub struct MergeIterInner { +pub(super) struct MergeIterInner { a: I, b: I, peeked: Option>, @@ -40,7 +40,7 @@ where impl MergeIterInner { /// Creates a new core for an iterator merging a pair of sources. - pub fn new(a: I, b: I) -> Self { + pub(super) fn new(a: I, b: I) -> Self { MergeIterInner { a, b, peeked: None } } @@ -51,7 +51,7 @@ impl MergeIterInner { /// the sources are not strictly ascending). If neither returned option /// contains a value, iteration has finished and subsequent calls will /// return the same empty pair. - pub fn nexts Ordering>( + pub(super) fn nexts Ordering>( &mut self, cmp: Cmp, ) -> (Option, Option) @@ -85,7 +85,7 @@ impl MergeIterInner { } /// Returns a pair of upper bounds for the `size_hint` of the final iterator. - pub fn lens(&self) -> (usize, usize) + pub(super) fn lens(&self) -> (usize, usize) where I: ExactSizeIterator, { diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index b8667d09c33b..665148066739 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -2,13 +2,13 @@ mod append; mod borrow; mod dedup_sorted_iter; mod fix; -pub mod map; +pub(super) mod map; mod mem; mod merge_iter; mod navigate; mod node; mod remove; mod search; -pub mod set; +pub(super) mod set; mod set_val; mod split; diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 14b7d4ad71f8..b2a7de74875d 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -7,7 +7,7 @@ use super::node::{Handle, NodeRef, marker}; use super::search::SearchBound; use crate::alloc::Allocator; // `front` and `back` are always both `None` or both `Some`. -pub struct LeafRange { +pub(super) struct LeafRange { front: Option, marker::Edge>>, back: Option, marker::Edge>>, } @@ -25,7 +25,7 @@ impl Default for LeafRange { } impl LeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LeafRange { front: None, back: None } } @@ -34,7 +34,7 @@ impl LeafRange { } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LeafRange, K, V> { + pub(super) fn reborrow(&self) -> LeafRange, K, V> { LeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -44,24 +44,24 @@ impl LeafRange { impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_checked(|kv| kv.into_kv()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_back_checked(|kv| kv.into_kv()) } } impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_back_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } } @@ -124,7 +124,7 @@ impl LazyLeafHandle { } // `front` and `back` are always both `None` or both `Some`. -pub struct LazyLeafRange { +pub(super) struct LazyLeafRange { front: Option>, back: Option>, } @@ -142,12 +142,12 @@ impl<'a, K: 'a, V: 'a> Clone for LazyLeafRange, K, V> { } impl LazyLeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LazyLeafRange { front: None, back: None } } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LazyLeafRange, K, V> { + pub(super) fn reborrow(&self) -> LazyLeafRange, K, V> { LazyLeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -157,24 +157,24 @@ impl LazyLeafRange { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -190,7 +190,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_unchecked( + pub(super) unsafe fn deallocating_next_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -200,7 +200,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_back_unchecked( + pub(super) unsafe fn deallocating_next_back_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -210,7 +210,7 @@ impl LazyLeafRange { } #[inline] - pub fn deallocating_end(&mut self, alloc: A) { + pub(super) fn deallocating_end(&mut self, alloc: A) { if let Some(front) = self.take_front() { front.deallocating_end(alloc) } @@ -313,7 +313,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -324,7 +324,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Finds the pair of leaf edges delimiting an entire tree. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { full_range(self, self) } } @@ -339,7 +339,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// # Safety /// Do not use the duplicate handles to visit the same KV twice. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -351,7 +351,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing mutation (of values only), so must be used /// with care. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { // We duplicate the root NodeRef here -- we will never visit the same KV // twice, and never end up with overlapping value references. let self2 = unsafe { ptr::read(&self) }; @@ -363,7 +363,7 @@ impl NodeRef { /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. - pub fn full_range(self) -> LazyLeafRange { + pub(super) fn full_range(self) -> LazyLeafRange { // We duplicate the root NodeRef here -- we will never access it in a way // that overlaps references obtained from the root. let self2 = unsafe { ptr::read(&self) }; @@ -377,7 +377,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. - pub fn next_kv( + pub(super) fn next_kv( self, ) -> Result< Handle, marker::KV>, @@ -398,7 +398,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the left side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. - pub fn next_back_kv( + pub(super) fn next_back_kv( self, ) -> Result< Handle, marker::KV>, @@ -627,7 +627,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn first_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -640,7 +642,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn last_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -651,7 +655,7 @@ impl NodeRef { +pub(super) enum Position { Leaf(NodeRef), Internal(NodeRef), InternalKV, @@ -661,7 +665,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Visits leaf nodes and internal KVs in order of ascending keys, and also /// visits internal nodes as a whole in a depth first order, meaning that /// internal nodes precede their individual KVs and their child nodes. - pub fn visit_nodes_in_order(self, mut visit: F) + pub(super) fn visit_nodes_in_order(self, mut visit: F) where F: FnMut(Position, K, V>), { @@ -693,7 +697,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Calculates the number of elements in a (sub)tree. - pub fn calc_length(self) -> usize { + pub(super) fn calc_length(self) -> usize { let mut result = 0; self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), @@ -708,7 +712,9 @@ impl Handle, marker::KV> { /// Returns the leaf edge closest to a KV for forward navigation. - pub fn next_leaf_edge(self) -> Handle, marker::Edge> { + pub(super) fn next_leaf_edge( + self, + ) -> Handle, marker::Edge> { match self.force() { Leaf(leaf_kv) => leaf_kv.right_edge(), Internal(internal_kv) => { @@ -719,7 +725,7 @@ impl } /// Returns the leaf edge closest to a KV for backward navigation. - pub fn next_back_leaf_edge( + pub(super) fn next_back_leaf_edge( self, ) -> Handle, marker::Edge> { match self.force() { @@ -735,7 +741,7 @@ impl impl NodeRef { /// Returns the leaf edge corresponding to the first point at which the /// given bound is true. - pub fn lower_bound( + pub(super) fn lower_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> @@ -758,7 +764,7 @@ impl NodeRef( + pub(super) fn upper_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 4057657632ba..6815ac1c1930 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -40,8 +40,8 @@ use crate::alloc::{Allocator, Layout}; use crate::boxed::Box; const B: usize = 6; -pub const CAPACITY: usize = 2 * B - 1; -pub const MIN_LEN_AFTER_SPLIT: usize = B - 1; +pub(super) const CAPACITY: usize = 2 * B - 1; +pub(super) const MIN_LEN_AFTER_SPLIT: usize = B - 1; const KV_IDX_CENTER: usize = B - 1; const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; @@ -179,7 +179,7 @@ type BoxedNode = NonNull>; /// as the returned reference is used. /// The methods supporting insert bend this rule by returning a raw pointer, /// i.e., a reference without any lifetime. -pub struct NodeRef { +pub(super) struct NodeRef { /// The number of levels that the node and the level of leaves are apart, a /// constant of the node that cannot be entirely described by `Type`, and that /// the node itself does not store. We only need to store the height of the root @@ -195,7 +195,7 @@ pub struct NodeRef { /// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. -pub type Root = NodeRef; +pub(super) type Root = NodeRef; impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { @@ -213,7 +213,7 @@ unsafe impl Send for NodeRef unsafe impl Send for NodeRef {} impl NodeRef { - pub fn new_leaf(alloc: A) -> Self { + pub(super) fn new_leaf(alloc: A) -> Self { Self::from_new_leaf(LeafNode::new(alloc)) } @@ -274,7 +274,7 @@ impl NodeRef { /// The number of edges is `len() + 1`. /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. - pub fn len(&self) -> usize { + pub(super) fn len(&self) -> usize { // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } @@ -285,12 +285,12 @@ impl NodeRef { /// root on top, the number says at which elevation the node appears. /// If you picture trees with leaves on top, the number says how high /// the tree extends above the node. - pub fn height(&self) -> usize { + pub(super) fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - pub fn reborrow(&self) -> NodeRef, K, V, Type> { + pub(super) fn reborrow(&self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -315,7 +315,7 @@ impl NodeRef /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn ascend( + pub(super) fn ascend( self, ) -> Result, marker::Edge>, Self> { const { @@ -335,24 +335,24 @@ impl NodeRef .ok_or(self) } - pub fn first_edge(self) -> Handle { + pub(super) fn first_edge(self) -> Handle { unsafe { Handle::new_edge(self, 0) } } - pub fn last_edge(self) -> Handle { + pub(super) fn last_edge(self) -> Handle { let len = self.len(); unsafe { Handle::new_edge(self, len) } } /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle { + pub(super) fn first_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, 0) } } /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle { + pub(super) fn last_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, len - 1) } @@ -381,7 +381,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } /// Borrows a view into the keys stored in the node. - pub fn keys(&self) -> &[K] { + pub(super) fn keys(&self) -> &[K] { let leaf = self.into_leaf(); unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } @@ -391,7 +391,7 @@ impl NodeRef { /// Similar to `ascend`, gets a reference to a node's parent node, but also /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend( + pub(super) unsafe fn deallocate_and_ascend( self, alloc: A, ) -> Option, marker::Edge>> { @@ -443,7 +443,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Returns a dormant copy of this node with its lifetime erased which can /// be reawakened later. - pub fn dormant(&self) -> NodeRef { + pub(super) fn dormant(&self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -455,7 +455,7 @@ impl NodeRef { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { + pub(super) unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -536,7 +536,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows exclusive access to the length of the node. - pub fn len_mut(&mut self) -> &mut u16 { + pub(super) fn len_mut(&mut self) -> &mut u16 { &mut self.as_leaf_mut().len } } @@ -578,14 +578,14 @@ impl NodeRef { impl NodeRef { /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new(alloc: A) -> Self { + pub(super) fn new(alloc: A) -> Self { NodeRef::new_leaf(alloc).forget_type() } /// Adds a new internal node with a single edge pointing to the previous root node, /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. - pub fn push_internal_level( + pub(super) fn push_internal_level( &mut self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -604,7 +604,7 @@ impl NodeRef { /// it will not invalidate other handles or references to the root node. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub fn pop_internal_level(&mut self, alloc: A) { + pub(super) fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); let top = self.node; @@ -628,18 +628,18 @@ impl NodeRef { /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe /// because the return value cannot be used to destroy the root, and there /// cannot be other references to the tree. - pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Slightly mutably borrows the owned root node. - pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Irreversibly transitions to a reference that permits traversal and offers /// destructive methods and little else. - pub fn into_dying(self) -> NodeRef { + pub(super) fn into_dying(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -651,7 +651,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// # Safety /// /// The returned handle has an unbound lifetime. - pub unsafe fn push_with_handle<'b>( + pub(super) unsafe fn push_with_handle<'b>( &mut self, key: K, val: V, @@ -672,7 +672,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node, and returns /// the mutable reference of the inserted value. - pub fn push(&mut self, key: K, val: V) -> *mut V { + pub(super) fn push(&mut self, key: K, val: V) -> *mut V { // SAFETY: The unbound handle is no longer accessible. unsafe { self.push_with_handle(key, val).into_val_mut() } } @@ -681,7 +681,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the right of that pair, /// to the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root) { + pub(super) fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); let len = self.len_mut(); @@ -699,21 +699,21 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { impl NodeRef { /// Removes any static information asserting that this node is a `Leaf` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Removes any static information asserting that this node is an `Internal` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< NodeRef, @@ -737,7 +737,9 @@ impl NodeRef { impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { + pub(super) unsafe fn cast_to_leaf_unchecked( + self, + ) -> NodeRef, K, V, marker::Leaf> { debug_assert!(self.height == 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -757,7 +759,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// a child node, these represent the spaces where child pointers would go between the key-value /// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one /// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle { +pub(super) struct Handle { node: Node, idx: usize, _marker: PhantomData, @@ -774,12 +776,12 @@ impl Clone for Handle { impl Handle { /// Retrieves the node that contains the edge or key-value pair this handle points to. - pub fn into_node(self) -> Node { + pub(super) fn into_node(self) -> Node { self.node } /// Returns the position of this handle in the node. - pub fn idx(&self) -> usize { + pub(super) fn idx(&self) -> usize { self.idx } } @@ -787,17 +789,17 @@ impl Handle { impl Handle, marker::KV> { /// Creates a new handle to a key-value pair in `node`. /// Unsafe because the caller must ensure that `idx < node.len()`. - pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { debug_assert!(idx < node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_edge(self) -> Handle, marker::Edge> { + pub(super) fn left_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx) } } - pub fn right_edge(self) -> Handle, marker::Edge> { + pub(super) fn right_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -815,7 +817,9 @@ impl Handle, HandleType> { /// Temporarily takes out another immutable handle on the same location. - pub fn reborrow(&self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) fn reborrow( + &self, + ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } } @@ -827,7 +831,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// dangerous. /// /// For details, see `NodeRef::reborrow_mut`. - pub unsafe fn reborrow_mut( + pub(super) unsafe fn reborrow_mut( &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type @@ -837,7 +841,9 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// Returns a dormant copy of this handle which can be reawakened later. /// /// See `DormantMutRef` for more details. - pub fn dormant(&self) -> Handle, HandleType> { + pub(super) fn dormant( + &self, + ) -> Handle, HandleType> { Handle { node: self.node.dormant(), idx: self.idx, _marker: PhantomData } } } @@ -849,7 +855,9 @@ impl Handle(self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) unsafe fn awaken<'a>( + self, + ) -> Handle, K, V, NodeType>, HandleType> { Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -857,13 +865,15 @@ impl Handle Handle, marker::Edge> { /// Creates a new handle to an edge in `node`. /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { debug_assert!(idx <= node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn left_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx > 0 { Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { @@ -871,7 +881,9 @@ impl Handle, mar } } - pub fn right_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn right_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx < self.node.len() { Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { @@ -880,7 +892,7 @@ impl Handle, mar } } -pub enum LeftOrRight { +pub(super) enum LeftOrRight { Left(T), Right(T), } @@ -1034,7 +1046,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// If the returned result is some `SplitResult`, the `left` field will be the root node. /// The returned pointer points to the inserted value, which in the case of `SplitResult` /// is in the `left` or `right` tree. - pub fn insert_recursing( + pub(super) fn insert_recursing( self, key: K, value: V, @@ -1078,7 +1090,7 @@ impl /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef { + pub(super) fn descend(self) -> NodeRef { const { assert!(BorrowType::TRAVERSAL_PERMIT); } @@ -1097,7 +1109,7 @@ impl } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv(self) -> (&'a K, &'a V) { + pub(super) fn into_kv(self) -> (&'a K, &'a V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf(); let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; @@ -1107,17 +1119,17 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn key_mut(&mut self) -> &mut K { + pub(super) fn key_mut(&mut self) -> &mut K { unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } - pub fn into_val_mut(self) -> &'a mut V { + pub(super) fn into_val_mut(self) -> &'a mut V { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + pub(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); let k = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; @@ -1127,13 +1139,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { unsafe { self.node.into_key_val_mut_at(self.idx) } } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub(super) fn kv_mut(&mut self) -> (&mut K, &mut V) { debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. @@ -1146,7 +1158,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } /// Replaces the key and value that the KV handle refers to. - pub fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + pub(super) fn replace_kv(&mut self, k: K, v: V) -> (K, V) { let (key, val) = self.kv_mut(); (mem::replace(key, k), mem::replace(val, v)) } @@ -1156,7 +1168,7 @@ impl Handle, marker::KV> /// Extracts the key and value that the KV handle refers to. /// # Safety /// The node that the handle refers to must not yet have been deallocated. - pub unsafe fn into_key_val(mut self) -> (K, V) { + pub(super) unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { @@ -1170,7 +1182,7 @@ impl Handle, marker::KV> /// # Safety /// The node that the handle refers to must not yet have been deallocated. #[inline] - pub unsafe fn drop_key_val(mut self) { + pub(super) unsafe fn drop_key_val(mut self) { // Run the destructor of the value even if the destructor of the key panics. struct Dropper<'a, T>(&'a mut MaybeUninit); impl Drop for Dropper<'_, T> { @@ -1229,7 +1241,10 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// - The key and value pointed to by this handle are extracted. /// - All the key-value pairs to the right of this handle are put into a newly /// allocated node. - pub fn split(mut self, alloc: A) -> SplitResult<'a, K, V, marker::Leaf> { + pub(super) fn split( + mut self, + alloc: A, + ) -> SplitResult<'a, K, V, marker::Leaf> { let mut new_node = LeafNode::new(alloc); let kv = self.split_leaf_data(&mut new_node); @@ -1240,7 +1255,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// Removes the key-value pair pointed to by this handle and returns it, along with the edge /// that the key-value pair collapsed into. - pub fn remove( + pub(super) fn remove( mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); @@ -1261,7 +1276,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// - The key and value pointed to by this handle are extracted. /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. - pub fn split( + pub(super) fn split( mut self, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { @@ -1285,14 +1300,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// Represents a session for evaluating and performing a balancing operation /// around an internal key-value pair. -pub struct BalancingContext<'a, K, V> { +pub(super) struct BalancingContext<'a, K, V> { parent: Handle, K, V, marker::Internal>, marker::KV>, left_child: NodeRef, K, V, marker::LeafOrInternal>, right_child: NodeRef, K, V, marker::LeafOrInternal>, } impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - pub fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { let self1 = unsafe { ptr::read(&self) }; let self2 = unsafe { ptr::read(&self) }; BalancingContext { @@ -1318,7 +1333,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// typically faster, since we only need to shift the node's N elements to /// the right, instead of shifting at least N of the sibling's elements to /// the left. - pub fn choose_parent_kv(self) -> Result>, Self> { + pub(super) fn choose_parent_kv(self) -> Result>, Self> { match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { @@ -1341,25 +1356,25 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { } impl<'a, K, V> BalancingContext<'a, K, V> { - pub fn left_child_len(&self) -> usize { + pub(super) fn left_child_len(&self) -> usize { self.left_child.len() } - pub fn right_child_len(&self) -> usize { + pub(super) fn right_child_len(&self) -> usize { self.right_child.len() } - pub fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.left_child } - pub fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.right_child } /// Returns whether merging is possible, i.e., whether there is enough room /// in a node to combine the central KV with both adjacent child nodes. - pub fn can_merge(&self) -> bool { + pub(super) fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } @@ -1433,7 +1448,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns the shrunk parent node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_parent( + pub(super) fn merge_tracking_parent( self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -1444,7 +1459,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns that child node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child( + pub(super) fn merge_tracking_child( self, alloc: A, ) -> NodeRef, K, V, marker::LeafOrInternal> { @@ -1456,7 +1471,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// where the tracked child edge ended up, /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child_edge( + pub(super) fn merge_tracking_child_edge( self, track_edge_idx: LeftOrRight, alloc: A, @@ -1479,7 +1494,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair into the right child. /// Returns a handle to the edge in the right child corresponding to where the original /// edge specified by `track_right_edge_idx` ended up. - pub fn steal_left( + pub(super) fn steal_left( mut self, track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1491,7 +1506,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair onto the left child. /// Returns a handle to the edge in the left child specified by `track_left_edge_idx`, /// which didn't move. - pub fn steal_right( + pub(super) fn steal_right( mut self, track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1500,7 +1515,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { + pub(super) fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1563,7 +1578,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { + pub(super) fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1628,7 +1643,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1636,7 +1651,7 @@ impl Handle, marker::E } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1644,7 +1659,7 @@ impl Handle, marke } impl Handle, marker::KV> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::KV> { unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } @@ -1653,7 +1668,7 @@ impl Handle, marker::K impl Handle, Type> { /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< Handle, Type>, @@ -1672,7 +1687,7 @@ impl Handle Handle, K, V, marker::LeafOrInternal>, Type> { /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked( + pub(super) unsafe fn cast_to_leaf_unchecked( self, ) -> Handle, K, V, marker::Leaf>, Type> { let node = unsafe { self.node.cast_to_leaf_unchecked() }; @@ -1683,7 +1698,7 @@ impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInterna impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { /// Move the suffix after `self` from one node to another one. `right` must be empty. /// The first edge of `right` remains unchanged. - pub fn move_suffix( + pub(super) fn move_suffix( &mut self, right: &mut NodeRef, K, V, marker::LeafOrInternal>, ) { @@ -1726,13 +1741,13 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma } } -pub enum ForceResult { +pub(super) enum ForceResult { Leaf(Leaf), Internal(Internal), } /// Result of insertion, when a node needed to expand beyond its capacity. -pub struct SplitResult<'a, K, V, NodeType> { +pub(super) struct SplitResult<'a, K, V, NodeType> { // Altered node in existing tree with elements and edges that belong to the left of `kv`. pub left: NodeRef, K, V, NodeType>, // Some key and value that existed before and were split off, to be inserted elsewhere. @@ -1742,32 +1757,32 @@ pub struct SplitResult<'a, K, V, NodeType> { } impl<'a, K, V> SplitResult<'a, K, V, marker::Leaf> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } impl<'a, K, V> SplitResult<'a, K, V, marker::Internal> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } -pub mod marker { +pub(super) mod marker { use core::marker::PhantomData; - pub enum Leaf {} - pub enum Internal {} - pub enum LeafOrInternal {} + pub(crate) enum Leaf {} + pub(crate) enum Internal {} + pub(crate) enum LeafOrInternal {} - pub enum Owned {} - pub enum Dying {} - pub enum DormantMut {} - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - pub struct ValMut<'a>(PhantomData<&'a mut ()>); + pub(crate) enum Owned {} + pub(crate) enum Dying {} + pub(crate) enum DormantMut {} + pub(crate) struct Immut<'a>(PhantomData<&'a ()>); + pub(crate) struct Mut<'a>(PhantomData<&'a mut ()>); + pub(crate) struct ValMut<'a>(PhantomData<&'a mut ()>); - pub trait BorrowType { + pub(crate) trait BorrowType { /// If node references of this borrow type allow traversing to other /// nodes in the tree, this constant is set to `true`. It can be used /// for a compile-time assertion. @@ -1786,8 +1801,8 @@ pub mod marker { impl<'a> BorrowType for ValMut<'a> {} impl BorrowType for DormantMut {} - pub enum KV {} - pub enum Edge {} + pub(crate) enum KV {} + pub(crate) enum Edge {} } /// Inserts a value into a slice of initialized elements followed by one uninitialized element. diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 4d2fa0f09417..ecd009f11c71 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -6,7 +6,7 @@ use crate::string::String; impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { // Asserts that the back pointer in each reachable node points to its parent. - pub fn assert_back_pointers(self) { + pub(crate) fn assert_back_pointers(self) { if let ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { let edge = unsafe { Handle::new_edge(node, idx) }; @@ -20,7 +20,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> // Renders a multi-line display of the keys in order and in tree hierarchy, // picturing the tree growing sideways from its root on the left to its // leaves on the right. - pub fn dump_keys(self) -> String + pub(crate) fn dump_keys(self) -> String where K: Debug, { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 56f2824b782b..9d870b86f34a 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -10,7 +10,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInter /// the leaf edge corresponding to that former pair. It's possible this empties /// a root node that is internal, which the caller should pop from the map /// holding the tree. The caller should also decrement the map's length. - pub fn remove_kv_tracking( + pub(super) fn remove_kv_tracking( self, handle_emptied_internal_root: F, alloc: A, diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 22e015edac3d..96e5bf108024 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -8,7 +8,7 @@ use SearchResult::*; use super::node::ForceResult::*; use super::node::{Handle, NodeRef, marker}; -pub enum SearchBound { +pub(super) enum SearchBound { /// An inclusive bound to look for, just like `Bound::Included(T)`. Included(T), /// An exclusive bound to look for, just like `Bound::Excluded(T)`. @@ -20,7 +20,7 @@ pub enum SearchBound { } impl SearchBound { - pub fn from_range(range_bound: Bound) -> Self { + pub(super) fn from_range(range_bound: Bound) -> Self { match range_bound { Bound::Included(t) => Included(t), Bound::Excluded(t) => Excluded(t), @@ -29,12 +29,12 @@ impl SearchBound { } } -pub enum SearchResult { +pub(super) enum SearchResult { Found(Handle, marker::KV>), GoDown(Handle, marker::Edge>), } -pub enum IndexResult { +pub(super) enum IndexResult { KV(usize), Edge(usize), } @@ -46,7 +46,7 @@ impl NodeRef( + pub(super) fn search_tree( mut self, key: &Q, ) -> SearchResult @@ -80,7 +80,7 @@ impl NodeRef( + pub(super) fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( mut self, range: &'r R, ) -> Result< @@ -156,7 +156,7 @@ impl NodeRef( + pub(super) fn find_lower_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -170,7 +170,7 @@ impl NodeRef( + pub(super) fn find_upper_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -192,7 +192,10 @@ impl NodeRef { /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn search_node(self, key: &Q) -> SearchResult + pub(super) fn search_node( + self, + key: &Q, + ) -> SearchResult where Q: Ord, K: Borrow, diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index c188ed1da611..87a79e6cf3f9 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -8,7 +8,7 @@ use super::search::SearchResult::*; impl Root { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. - pub fn calc_split_length( + pub(super) fn calc_split_length( total_num: usize, root_a: &Root, root_b: &Root, @@ -31,7 +31,11 @@ impl Root { /// and if the ordering of `Q` corresponds to that of `K`. /// If `self` respects all `BTreeMap` tree invariants, then both /// `self` and the returned tree will respect those invariants. - pub fn split_off(&mut self, key: &Q, alloc: A) -> Self + pub(super) fn split_off( + &mut self, + key: &Q, + alloc: A, + ) -> Self where K: Borrow, { diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index b7d4f8512a0f..aa19239f6c55 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -58,7 +58,7 @@ fn list_from(v: &[T]) -> LinkedList { v.iter().cloned().collect() } -pub fn check_links(list: &LinkedList) { +fn check_links(list: &LinkedList) { unsafe { let mut len = 0; let mut last_ptr: Option<&Node> = None; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index b4f08debc932..8af137f644f1 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -88,6 +88,7 @@ #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // // Library features: // tidy-alphabetical-start @@ -225,7 +226,7 @@ pub mod alloc; pub mod boxed; #[cfg(test)] mod boxed { - pub use std::boxed::Box; + pub(crate) use std::boxed::Box; } pub mod borrow; pub mod collections; diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index ad86bf4bf072..b80d1fc78894 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -97,7 +97,7 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self::new_in(Global) } @@ -120,7 +120,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity(capacity: usize) -> Self { + pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -129,7 +129,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity_zeroed(capacity: usize) -> Self { + pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), _marker: PhantomData, @@ -172,7 +172,7 @@ impl RawVec { /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[inline] - pub const fn new_in(alloc: A) -> Self { + pub(crate) const fn new_in(alloc: A) -> Self { Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } @@ -181,7 +181,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -191,7 +191,7 @@ impl RawVec { /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] - pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { Ok(inner) => Ok(Self { inner, _marker: PhantomData }), Err(e) => Err(e), @@ -203,7 +203,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -222,7 +222,7 @@ impl RawVec { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { + pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -247,7 +247,7 @@ impl RawVec { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -265,7 +265,7 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -278,12 +278,12 @@ impl RawVec { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub const fn ptr(&self) -> *mut T { + pub(crate) const fn ptr(&self) -> *mut T { self.inner.ptr() } #[inline] - pub fn non_null(&self) -> NonNull { + pub(crate) fn non_null(&self) -> NonNull { self.inner.non_null() } @@ -291,13 +291,13 @@ impl RawVec { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub const fn capacity(&self) -> usize { + pub(crate) const fn capacity(&self) -> usize { self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub fn allocator(&self) -> &A { + pub(crate) fn allocator(&self) -> &A { self.inner.allocator() } @@ -323,7 +323,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn reserve(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -332,12 +332,16 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] #[track_caller] - pub fn grow_one(&mut self) { + pub(crate) fn grow_one(&mut self) { self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + pub(crate) fn try_reserve( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { self.inner.try_reserve(len, additional, T::LAYOUT) } @@ -360,12 +364,12 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[track_caller] - pub fn reserve_exact(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub fn try_reserve_exact( + pub(crate) fn try_reserve_exact( &mut self, len: usize, additional: usize, @@ -386,7 +390,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[track_caller] #[inline] - pub fn shrink_to_fit(&mut self, cap: usize) { + pub(crate) fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index edc8d99f2f99..1cedead7aa24 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -85,6 +85,7 @@ use crate::vec::Vec; // functions are actually methods that are in `impl [T]` but not in // `core::slice::SliceExt` - we need to supply these functions for the // `test_permutations` test +#[allow(unreachable_pub)] // cfg(test) pub above pub(crate) mod hack { use core::alloc::Allocator; diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs index 684bac60d9a8..8e00e4f41e5d 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -11,7 +11,7 @@ use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate /// Crash test dummies are identified and ordered by an id, so they can be used /// as keys in a BTreeMap. #[derive(Debug)] -pub struct CrashTestDummy { +pub(crate) struct CrashTestDummy { pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, @@ -20,7 +20,7 @@ pub struct CrashTestDummy { impl CrashTestDummy { /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub fn new(id: usize) -> CrashTestDummy { + pub(crate) fn new(id: usize) -> CrashTestDummy { CrashTestDummy { id, cloned: AtomicUsize::new(0), @@ -31,34 +31,34 @@ impl CrashTestDummy { /// Creates an instance of a crash test dummy that records what events it experiences /// and optionally panics. - pub fn spawn(&self, panic: Panic) -> Instance<'_> { + pub(crate) fn spawn(&self, panic: Panic) -> Instance<'_> { Instance { origin: self, panic } } /// Returns how many times instances of the dummy have been cloned. - pub fn cloned(&self) -> usize { + pub(crate) fn cloned(&self) -> usize { self.cloned.load(SeqCst) } /// Returns how many times instances of the dummy have been dropped. - pub fn dropped(&self) -> usize { + pub(crate) fn dropped(&self) -> usize { self.dropped.load(SeqCst) } /// Returns how many times instances of the dummy have had their `query` member invoked. - pub fn queried(&self) -> usize { + pub(crate) fn queried(&self) -> usize { self.queried.load(SeqCst) } } #[derive(Debug)] -pub struct Instance<'a> { +pub(crate) struct Instance<'a> { origin: &'a CrashTestDummy, panic: Panic, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Panic { +pub(crate) enum Panic { Never, InClone, InDrop, @@ -66,12 +66,12 @@ pub enum Panic { } impl Instance<'_> { - pub fn id(&self) -> usize { + pub(crate) fn id(&self) -> usize { self.origin.id } /// Some anonymous query, the result of which is already given. - pub fn query(&self, result: R) -> R { + pub(crate) fn query(&self, result: R) -> R { self.origin.queried.fetch_add(1, SeqCst); if self.panic == Panic::InQuery { panic!("panic in `query`"); diff --git a/library/alloc/src/testing/mod.rs b/library/alloc/src/testing/mod.rs index 7a094f8a5952..c8457daf93e5 100644 --- a/library/alloc/src/testing/mod.rs +++ b/library/alloc/src/testing/mod.rs @@ -1,3 +1,3 @@ -pub mod crash_test; -pub mod ord_chaos; -pub mod rng; +pub(crate) mod crash_test; +pub(crate) mod ord_chaos; +pub(crate) mod rng; diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloc/src/testing/ord_chaos.rs index 96ce7c157904..55e1ae5e3dea 100644 --- a/library/alloc/src/testing/ord_chaos.rs +++ b/library/alloc/src/testing/ord_chaos.rs @@ -4,7 +4,7 @@ use std::ptr; // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] -pub enum Cyclic3 { +pub(crate) enum Cyclic3 { A, B, C, @@ -37,16 +37,16 @@ impl Eq for Cyclic3 {} // Controls the ordering of values wrapped by `Governed`. #[derive(Debug)] -pub struct Governor { +pub(crate) struct Governor { flipped: Cell, } impl Governor { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Governor { flipped: Cell::new(false) } } - pub fn flip(&self) { + pub(crate) fn flip(&self) { self.flipped.set(!self.flipped.get()); } } @@ -55,7 +55,7 @@ impl Governor { // (assuming that `T` respects total order), but can suddenly be made to invert // that total order. #[derive(Debug)] -pub struct Governed<'a, T>(pub T, pub &'a Governor); +pub(crate) struct Governed<'a, T>(pub T, pub &'a Governor); impl PartialOrd for Governed<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/library/alloc/src/testing/rng.rs b/library/alloc/src/testing/rng.rs index ecf543bee035..77d3348f38a5 100644 --- a/library/alloc/src/testing/rng.rs +++ b/library/alloc/src/testing/rng.rs @@ -1,5 +1,5 @@ /// XorShiftRng -pub struct DeterministicRng { +pub(crate) struct DeterministicRng { count: usize, x: u32, y: u32, @@ -8,12 +8,12 @@ pub struct DeterministicRng { } impl DeterministicRng { - pub fn new() -> Self { + pub(crate) fn new() -> Self { DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } /// Guarantees that each returned number is unique. - pub fn next(&mut self) -> u32 { + pub(crate) fn next(&mut self) -> u32 { self.count += 1; assert!(self.count <= 70029); let x = self.x; From 0c3deeb24693cfa295701215023f87852bf34cc0 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 20 Jan 2025 09:43:10 +0100 Subject: [PATCH 068/342] `match_bool`: omit suggestion if guard is present Without this check, the lint would suggest that ```rust match test { true if option == 5 => 10, _ => 1, }; ``` is replaced by `if test { 10 } else { 1 }`. --- clippy_lints/src/matches/match_bool.rs | 51 +++++++------- tests/ui/match_bool.fixed | 58 ++++++++++++++++ tests/ui/match_bool.rs | 44 ++++++++++-- tests/ui/match_bool.stderr | 96 +++++++++++++++++++------- 4 files changed, 195 insertions(+), 54 deletions(-) create mode 100644 tests/ui/match_bool.fixed diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index 69105ff0d5c7..02e12eb53fcf 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_unit_expr; -use clippy_utils::source::{expr_block, snippet}; +use clippy_utils::source::expr_block; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -20,14 +20,25 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] "you seem to be trying to match on a boolean expression", move |diag| { if arms.len() == 2 { - // no guards - let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { + let mut app = Applicability::MachineApplicable; + let test_sugg = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { + let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app); if let ExprKind::Lit(lit) = arm_bool.kind { - match lit.node { - LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), - LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), + match &lit.node { + LitKind::Bool(true) => Some(test), + LitKind::Bool(false) => Some(!test), _ => None, } + .map(|test| { + if let Some(guard) = &arms[0] + .guard + .map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app)) + { + test.and(guard) + } else { + test + } + }) } else { None } @@ -35,39 +46,31 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] None }; - if let Some((true_expr, false_expr)) = exprs { - let mut app = Applicability::HasPlaceholders; + if let Some(test_sugg) = test_sugg { let ctxt = expr.span.ctxt(); + let (true_expr, false_expr) = (arms[0].body, arms[1].body); let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { (false, false) => Some(format!( "if {} {} else {}", - snippet(cx, scrutinee.span, "b"), + test_sugg, expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app), expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) )), (false, true) => Some(format!( "if {} {}", - snippet(cx, scrutinee.span, "b"), + test_sugg, expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app) )), - (true, false) => { - let test = Sugg::hir(cx, scrutinee, ".."); - Some(format!( - "if {} {}", - !test, - expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) - )) - }, + (true, false) => Some(format!( + "if {} {}", + !test_sugg, + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) + )), (true, true) => None, }; if let Some(sugg) = sugg { - diag.span_suggestion( - expr.span, - "consider using an `if`/`else` expression", - sugg, - Applicability::HasPlaceholders, - ); + diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app); } } } diff --git a/tests/ui/match_bool.fixed b/tests/ui/match_bool.fixed new file mode 100644 index 000000000000..61a8e54fa10c --- /dev/null +++ b/tests/ui/match_bool.fixed @@ -0,0 +1,58 @@ +#![deny(clippy::match_bool)] +#![allow(clippy::nonminimal_bool, clippy::eq_op)] + +fn match_bool() { + let test: bool = true; + + if test { 0 } else { 42 }; + + let option = 1; + if option == 1 { 1 } else { 0 }; + + if !test { + println!("Noooo!"); + }; + + if !test { + println!("Noooo!"); + }; + + if !(test && test) { + println!("Noooo!"); + }; + + if !test { + println!("Noooo!"); + } else { + println!("Yes!"); + }; + + // Not linted + match option { + 1..=10 => 1, + 11..=20 => 2, + _ => 3, + }; + + // Don't lint + let _ = match test { + #[cfg(feature = "foo")] + true if option == 5 => 10, + true => 0, + false => 1, + }; + + let _ = if test && option == 5 { 10 } else { 1 }; + + let _ = if !test && option == 5 { 10 } else { 1 }; + + if test && option == 5 { println!("Hello") }; + + if !(test && option == 5) { println!("Hello") }; + + if !test && option == 5 { println!("Hello") }; + + if !(!test && option == 5) { println!("Hello") }; +} + +fn main() {} diff --git a/tests/ui/match_bool.rs b/tests/ui/match_bool.rs index f84af393e47f..4952a225c24b 100644 --- a/tests/ui/match_bool.rs +++ b/tests/ui/match_bool.rs @@ -1,5 +1,5 @@ -//@no-rustfix: overlapping suggestions #![deny(clippy::match_bool)] +#![allow(clippy::nonminimal_bool, clippy::eq_op)] fn match_bool() { let test: bool = true; @@ -34,11 +34,7 @@ fn match_bool() { }; match test && test { - //~^ ERROR: this boolean expression can be simplified - //~| NOTE: `-D clippy::nonminimal-bool` implied by `-D warnings` - //~| ERROR: you seem to be trying to match on a boolean expression - //~| ERROR: equal expressions as operands to `&&` - //~| NOTE: `#[deny(clippy::eq_op)]` on by default + //~^ ERROR: you seem to be trying to match on a boolean expression false => { println!("Noooo!"); }, @@ -69,6 +65,42 @@ fn match_bool() { true => 0, false => 1, }; + + let _ = match test { + //~^ ERROR: you seem to be trying to match on a boolean expression + true if option == 5 => 10, + _ => 1, + }; + + let _ = match test { + //~^ ERROR: you seem to be trying to match on a boolean expression + false if option == 5 => 10, + _ => 1, + }; + + match test { + //~^ ERROR: you seem to be trying to match on a boolean expression + true if option == 5 => println!("Hello"), + _ => (), + }; + + match test { + //~^ ERROR: you seem to be trying to match on a boolean expression + true if option == 5 => (), + _ => println!("Hello"), + }; + + match test { + //~^ ERROR: you seem to be trying to match on a boolean expression + false if option == 5 => println!("Hello"), + _ => (), + }; + + match test { + //~^ ERROR: you seem to be trying to match on a boolean expression + false if option == 5 => (), + _ => println!("Hello"), + }; } fn main() {} diff --git a/tests/ui/match_bool.stderr b/tests/ui/match_bool.stderr index fb24e67eceef..f76c79cd7f7f 100644 --- a/tests/ui/match_bool.stderr +++ b/tests/ui/match_bool.stderr @@ -1,12 +1,3 @@ -error: this boolean expression can be simplified - --> tests/ui/match_bool.rs:36:11 - | -LL | match test && test { - | ^^^^^^^^^^^^ help: try: `test` - | - = note: `-D clippy::nonminimal-bool` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` - error: you seem to be trying to match on a boolean expression --> tests/ui/match_bool.rs:7:5 | @@ -18,7 +9,7 @@ LL | | }; | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }` | note: the lint level is defined here - --> tests/ui/match_bool.rs:2:9 + --> tests/ui/match_bool.rs:1:9 | LL | #![deny(clippy::match_bool)] | ^^^^^^^^^^^^^^^^^^ @@ -75,7 +66,10 @@ error: you seem to be trying to match on a boolean expression --> tests/ui/match_bool.rs:36:5 | LL | / match test && test { -... | +LL | | +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, LL | | _ => (), LL | | }; | |_____^ @@ -87,16 +81,8 @@ LL + println!("Noooo!"); LL ~ }; | -error: equal expressions as operands to `&&` - --> tests/ui/match_bool.rs:36:11 - | -LL | match test && test { - | ^^^^^^^^^^^^ - | - = note: `#[deny(clippy::eq_op)]` on by default - error: you seem to be trying to match on a boolean expression - --> tests/ui/match_bool.rs:48:5 + --> tests/ui/match_bool.rs:44:5 | LL | / match test { LL | | @@ -109,12 +95,74 @@ LL | | }; | help: consider using an `if`/`else` expression | -LL ~ if test { -LL + println!("Yes!"); -LL + } else { +LL ~ if !test { LL + println!("Noooo!"); +LL + } else { +LL + println!("Yes!"); LL ~ }; | -error: aborting due to 8 previous errors +error: you seem to be trying to match on a boolean expression + --> tests/ui/match_bool.rs:69:13 + | +LL | let _ = match test { + | _____________^ +LL | | +LL | | true if option == 5 => 10, +LL | | _ => 1, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test && option == 5 { 10 } else { 1 }` + +error: you seem to be trying to match on a boolean expression + --> tests/ui/match_bool.rs:75:13 + | +LL | let _ = match test { + | _____________^ +LL | | +LL | | false if option == 5 => 10, +LL | | _ => 1, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !test && option == 5 { 10 } else { 1 }` + +error: you seem to be trying to match on a boolean expression + --> tests/ui/match_bool.rs:81:5 + | +LL | / match test { +LL | | +LL | | true if option == 5 => println!("Hello"), +LL | | _ => (), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test && option == 5 { println!("Hello") }` + +error: you seem to be trying to match on a boolean expression + --> tests/ui/match_bool.rs:87:5 + | +LL | / match test { +LL | | +LL | | true if option == 5 => (), +LL | | _ => println!("Hello"), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !(test && option == 5) { println!("Hello") }` + +error: you seem to be trying to match on a boolean expression + --> tests/ui/match_bool.rs:93:5 + | +LL | / match test { +LL | | +LL | | false if option == 5 => println!("Hello"), +LL | | _ => (), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !test && option == 5 { println!("Hello") }` + +error: you seem to be trying to match on a boolean expression + --> tests/ui/match_bool.rs:99:5 + | +LL | / match test { +LL | | +LL | | false if option == 5 => (), +LL | | _ => println!("Hello"), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !(!test && option == 5) { println!("Hello") }` + +error: aborting due to 12 previous errors From 1d5af89654d69024e1bcf3c326b6c53a6878b511 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:05:19 -0500 Subject: [PATCH 069/342] fix: Only refresh syntax tree view when the active document changes --- src/tools/rust-analyzer/editors/code/src/ctx.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 5550bfa6558a..96dc4f19b82c 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -361,7 +361,14 @@ export class Ctx implements RustAnalyzerExtensionApi { } }); - vscode.workspace.onDidChangeTextDocument(async () => { + vscode.workspace.onDidChangeTextDocument(async (e) => { + if ( + vscode.window.activeTextEditor?.document !== e.document || + e.contentChanges.length === 0 + ) { + return; + } + if (this.syntaxTreeView?.visible) { await this.syntaxTreeProvider?.refresh(); } From f98e9715200133d0c7f25997cdd37ab7ea2a7247 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 20 Jan 2025 21:21:42 +0200 Subject: [PATCH 070/342] Fix another bug with completion of trait items inside macros This time, when completing the keyword (e.g. `fn` + whitespace). The bug was actually a double-bug: First, we did not resolve the impl in the macro-expanded file but in the real file, which of course cannot work. Second, in analysis the whitespace was correlated with the `impl` and not the incomplete `fn`, which caused fake (where we insert an identifier after the whitespace) and real expansions to go out of sync, which failed analysis. The fix is to skip whitespaces in analysis. --- .../src/completions/item_list/trait_impl.rs | 32 +++++++++++++++---- .../ide-completion/src/context/analysis.rs | 15 ++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 246b12526698..18629529b75b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -85,7 +85,7 @@ fn complete_trait_impl_name( name: &Option, kind: ImplCompletionKind, ) -> Option<()> { - let item = match name { + let macro_file_item = match name { Some(name) => name.syntax().parent(), None => { let token = &ctx.token; @@ -96,12 +96,12 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node_rooted(&item)?; + let real_file_item = ctx.sema.original_syntax_node_rooted(¯o_file_item)?; // item -> ASSOC_ITEM_LIST -> IMPL - let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; + let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?; let replacement_range = { // ctx.sema.original_ast_node(item)?; - let first_child = item + let first_child = real_file_item .children_with_tokens() .find(|child| { !matches!( @@ -109,7 +109,7 @@ fn complete_trait_impl_name( SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR ) }) - .unwrap_or_else(|| SyntaxElement::Node(item.clone())); + .unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone())); TextRange::new(first_child.text_range().start(), ctx.source_range().end()) }; @@ -1658,7 +1658,7 @@ trait Trait { impl Trait for () { f$0 } - "#, + "#, expect![[r#" me fn bar(..) me fn baz(..) @@ -1668,5 +1668,25 @@ impl Trait for () { kw self:: "#]], ); + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + fn $0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + "#]], + ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 3c4d489c0ff8..562f60dbe2bb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -5,7 +5,7 @@ use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use itertools::Either; use syntax::{ - algo::{ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, + algo::{self, ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, ast::{ self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, NameOrNameRef, @@ -85,6 +85,11 @@ pub(super) fn expand_and_analyze( }) } +fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option { + let token = file.token_at_offset(offset).left_biased()?; + algo::skip_whitespace_token(token, Direction::Prev) +} + /// Expand attributes and macro calls at the current cursor position for both the original file /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and speculative states stay in sync. @@ -125,9 +130,7 @@ fn expand( // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) - && original_file - .token_at_offset(original_offset + relative_offset) - .left_biased() + && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. @@ -143,9 +146,11 @@ fn expand( let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); + let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset) + .and_then(|token| token.parent_ancestors().find_map(ast::Item::cast)); let ancestor_items = iter::successors( Option::zip( - find_node_at_offset::(&original_file, original_offset), + original_node, find_node_at_offset::( &speculative_file, fake_ident_token.text_range().start(), From a56f9ad574773c06c511b0caee89382494ee24db Mon Sep 17 00:00:00 2001 From: dianne Date: Thu, 2 Jan 2025 21:47:54 -0800 Subject: [PATCH 071/342] remove Rule 3 from `ref_pat_eat_one_layer_2024` The debug assertion ensuring that the pattern mutability cap holds assumes the presence of Rule 3, so it now checks for that. I considered going back to only tracking the mutability cap when Rule 3 is present, but since the mutability cap is used in Rule 5's implementation too, the debug assertion would still need to check which typing rules are present. This also required some changes to tests: - `ref_pat_eat_one_layer_2021.rs` had a test for Rule 3; I'll be handling tests for earlier editions in a later commit, so as a stopgap I've #[cfg]ed it out. - One test case had to be moved from `well-typed-edition-2024.rs` to `borrowck-errors.rs` in order to get borrowck to run on it and emit an error. --- compiler/rustc_hir_typeck/src/pat.rs | 8 +++--- .../borrowck-errors.classic.stderr | 8 +++++- .../experimental/borrowck-errors.rs | 5 ++++ .../pattern-errors.classic.stderr | 26 +------------------ .../experimental/pattern-errors.rs | 4 +-- .../ref_pat_eat_one_layer_2021.rs | 2 ++ .../experimental/well-typed-edition-2024.rs | 3 --- 7 files changed, 22 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index cbd1db2ca255..cf9921312d52 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -243,8 +243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn downgrade_mut_inside_shared(&self) -> bool { // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior // across all editions, this may be removed. - self.tcx.features().ref_pat_eat_one_layer_2024() - || self.tcx.features().ref_pat_eat_one_layer_2024_structural() + self.tcx.features().ref_pat_eat_one_layer_2024_structural() } /// Experimental pattern feature: when do reference patterns match against inherited references? @@ -425,7 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { max_ref_mutbl: MutblCap, ) -> (Ty<'tcx>, ByRef, MutblCap) { #[cfg(debug_assertions)] - if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut { + if def_br == ByRef::Yes(Mutability::Mut) + && max_ref_mutbl != MutblCap::Mut + && self.downgrade_mut_inside_shared() + { span_bug!(pat.span, "Pattern mutability cap violated!"); } match adjust_mode { diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr index c62461140750..f26151fe4f70 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr @@ -19,7 +19,13 @@ error[E0596]: cannot borrow data in a `&` reference as mutable LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable -error: aborting due to 2 previous errors +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:17:23 + | +LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { + | ^ cannot borrow as mutable + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0507, E0596. For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs index a01e9ca26576..79581936418a 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -13,4 +13,9 @@ pub fn main() { let &ref mut x = &0; //~^ cannot borrow data in a `&` reference as mutable [E0596] + + if let &Some(Some(x)) = &Some(&mut Some(0)) { + //[classic]~^ ERROR: cannot borrow data in a `&` reference as mutable + let _: &u32 = x; + } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr index 2bc3ecb7636d..9b8a1e0ff39b 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr @@ -46,18 +46,6 @@ help: replace this `&mut` pattern with `&` LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:31:23 - | -LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { - | ~ - error[E0308]: mismatched types --> $DIR/pattern-errors.rs:34:23 | @@ -70,18 +58,6 @@ help: replace this `&mut` pattern with `&` LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:37:29 - | -LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) { - | ~ - error[E0308]: mismatched types --> $DIR/pattern-errors.rs:40:17 | @@ -106,6 +82,6 @@ help: replace this `&mut` pattern with `&` LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index 3535ba9c7019..f9cc8504d554 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -29,13 +29,13 @@ pub fn main() { //~^ ERROR: mismatched types } if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - //~^ ERROR: mismatched types + //[structural]~^ ERROR: mismatched types } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types } if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - //~^ ERROR: mismatched types + //[structural]~^ ERROR: mismatched types } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs index 9372049a2b28..ab3264704acc 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs @@ -6,9 +6,11 @@ #![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { + #[cfg(structural)] if let &Some(Some(x)) = &Some(&mut Some(0)) { let _: &u32 = x; } + if let Some(&x) = Some(&mut 0) { let _: u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index 077b52d8f274..28e008a779b1 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -35,9 +35,6 @@ pub fn main() { if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { let _: &u32 = x; } - if let &Some(Some(x)) = &Some(&mut Some(0)) { - let _: &u32 = x; - } if let Some(&Some(&x)) = &Some(&mut Some(0)) { let _: u32 = x; } From c57708a58dbb7fbf3605dcc33d03d4e1d33747e3 Mon Sep 17 00:00:00 2001 From: dianne Date: Sat, 4 Jan 2025 01:38:17 -0800 Subject: [PATCH 072/342] add more tests where the rulesets disagree These come directly from the "Compare" tab of Typing Rust Patterns, though they had to be split across multiple files. They're not comprehensive, but they do provide some previously-missing coverage and are easier to check against the spec. Possibly they should be split up some more, since `pattern-errors.rs` is getting a bit unwieldy, but I'm not sure how best to go about that. --- .../borrowck-errors.classic.stderr | 52 ++- .../experimental/borrowck-errors.rs | 16 + .../pattern-errors.classic.stderr | 50 ++- .../experimental/pattern-errors.rs | 92 +++++ .../pattern-errors.structural.stderr | 319 +++++++++++++++++- .../ref-binding-on-inh-ref-errors.rs | 40 +++ ...inding-on-inh-ref-errors.structural.stderr | 74 ++++ ...ef-mut-inside-shared-ref-pat.classic.fixed | 4 + ...f-mut-inside-shared-ref-pat.classic.stderr | 10 +- .../ref-mut-inside-shared-ref-pat.rs | 4 + ...mut-inside-shared-ref-pat.structural.fixed | 4 + 11 files changed, 659 insertions(+), 6 deletions(-) create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr index f26151fe4f70..65b03f7a6109 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr @@ -25,7 +25,55 @@ error[E0596]: cannot borrow data in a `&` reference as mutable LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { | ^ cannot borrow as mutable -error: aborting due to 3 previous errors +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:22:11 + | +LL | let &[x] = &&mut [0]; + | ^ cannot borrow as mutable -Some errors have detailed explanations: E0507, E0596. +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:26:16 + | +LL | let [&x] = &[&mut 0]; + | - ^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&ref x] = &[&mut 0]; + | +++ + +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:31:16 + | +LL | let [&x] = &mut [&mut 0]; + | - ^^^^^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&ref x] = &mut [&mut 0]; + | +++ + +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:34:20 + | +LL | let [&mut x] = &mut [&mut 0]; + | - ^^^^^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&mut ref x] = &mut [&mut 0]; + | +++ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0507, E0508, E0596. For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs index 79581936418a..7645c145b652 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -18,4 +18,20 @@ pub fn main() { //[classic]~^ ERROR: cannot borrow data in a `&` reference as mutable let _: &u32 = x; } + + let &[x] = &&mut [0]; + //[classic]~^ ERROR: cannot borrow data in a `&` reference as mutable + let _: &u32 = x; + + let [&x] = &[&mut 0]; + //[classic]~^ ERROR: cannot move out of type + let _: &u32 = x; + + #[cfg(classic)] // TODO: this should pass on `structural` but doesn't + let [&x] = &mut [&mut 0]; //[classic]~ ERROR: cannot move out of type + let _: &u32 = x; + + let [&mut x] = &mut [&mut 0]; + //[classic]~^ ERROR: cannot move out of type + let _: &mut u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr index 9b8a1e0ff39b..fbb9a35443a2 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr @@ -82,6 +82,54 @@ help: replace this `&mut` pattern with `&` LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ -error: aborting due to 7 previous errors +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:119:10 + | +LL | let [&mut x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | let [&x] = &[&mut 0]; + | ~ + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:124:10 + | +LL | let [&mut &x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | let [&&x] = &[&mut 0]; + | ~ + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:129:10 + | +LL | let [&mut &ref x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | let [&&ref x] = &[&mut 0]; + | ~ + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:134:10 + | +LL | let [&mut &(mut x)] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | let [&&(mut x)] = &[&mut 0]; + | ~ + +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index f9cc8504d554..b5e8bd112aa8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -44,3 +44,95 @@ pub fn main() { //~^ ERROR: mismatched types } } + +// TODO: these should be mutability mismatches on `structural` +fn structural_errors_0() { + let &[&mut x] = &&mut [0]; + //[structural]~^ ERROR: mismatched types + let _: u32 = x; + //[structural]~^ ERROR: mismatched types + + let &[&mut x] = &mut &mut [0]; + //[structural]~^ ERROR: mismatched types + let _: u32 = x; + //[structural]~^ ERROR: mismatched types + + let &[&mut ref x] = &&mut [0]; + //[structural]~^ ERROR: mismatched types + let _: &u32 = x; + + let &[&mut ref x] = &mut &mut [0]; + //[structural]~^ ERROR: mismatched types + let _: &u32 = x; + + let &[&mut mut x] = &&mut [0]; + //[structural]~^ ERROR: mismatched types + //[structural]~| ERROR: binding cannot be both mutable and by-reference + let _: u32 = x; + //[structural]~^ ERROR: mismatched types + + let &[&mut mut x] = &mut &mut [0]; + //[structural]~^ ERROR: mismatched types + //[structural]~| ERROR: binding cannot be both mutable and by-reference + let _: u32 = x; + //[structural]~^ ERROR: mismatched types +} + +fn structural_errors_1() { + let [&(mut x)] = &[&0]; + //[structural]~^ ERROR: binding cannot be both mutable and by-reference + let _: &u32 = x; + + let [&(mut x)] = &mut [&0]; + //[structural]~^ ERROR: binding cannot be both mutable and by-reference + let _: &u32 = x; +} + +// TODO: these should be mutability mismatches on `structural` +fn structural_errors_2() { + let [&&mut x] = &[&mut 0]; + //[structural]~^ ERROR: mismatched types + let _: u32 = x; + //[structural]~^ ERROR: mismatched types + + let [&&mut x] = &mut [&mut 0]; + let _: u32 = x; + + let [&&mut ref x] = &[&mut 0]; + //[structural]~^ ERROR: mismatched types + let _: &u32 = x; + + let [&&mut ref x] = &mut [&mut 0]; + let _: &u32 = x; + + let [&&mut mut x] = &[&mut 0]; + //[structural]~^ ERROR: binding cannot be both mutable and by-reference + //[structural]~| ERROR: mismatched types + let _: u32 = x; + //[structural]~^ ERROR: mismatched types + + let [&&mut mut x] = &mut [&mut 0]; + let _: u32 = x; +} + +fn classic_errors_0() { + let [&mut x] = &[&mut 0]; + //[classic]~^ ERROR: mismatched types + //[classic]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let [&mut &x] = &[&mut 0]; + //[classic]~^ ERROR: mismatched types + //[classic]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let [&mut &ref x] = &[&mut 0]; + //[classic]~^ ERROR: mismatched types + //[classic]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let [&mut &(mut x)] = &[&mut 0]; + //[classic]~^ ERROR: mismatched types + //[classic]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr index 59d65553fae8..4eabb8b1b0a3 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr @@ -84,6 +84,321 @@ LL | if let Some(&mut Some(x)) = &Some(Some(0)) { = note: expected enum `Option<{integer}>` found mutable reference `&mut _` -error: aborting due to 7 previous errors +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:50:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:50:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut x] = &&mut [0]; +LL + let &[x] = &&mut [0]; + | -For more information about this error, try `rustc --explain E0308`. +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:52:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:55:11 + | +LL | let &[&mut x] = &mut &mut [0]; + | ^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:55:11 + | +LL | let &[&mut x] = &mut &mut [0]; + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut x] = &mut &mut [0]; +LL + let &[x] = &mut &mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:57:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:60:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:60:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut ref x] = &&mut [0]; +LL + let &[ref x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:64:11 + | +LL | let &[&mut ref x] = &mut &mut [0]; + | ^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:64:11 + | +LL | let &[&mut ref x] = &mut &mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut ref x] = &mut &mut [0]; +LL + let &[ref x] = &mut &mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:68:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:68:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut mut x] = &&mut [0]; +LL + let &[mut x] = &&mut [0]; + | + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:68:16 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:71:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:74:11 + | +LL | let &[&mut mut x] = &mut &mut [0]; + | ^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:74:11 + | +LL | let &[&mut mut x] = &mut &mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut mut x] = &mut &mut [0]; +LL + let &[mut x] = &mut &mut [0]; + | + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:74:16 + | +LL | let &[&mut mut x] = &mut &mut [0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:77:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:82:12 + | +LL | let [&(mut x)] = &[&0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:86:12 + | +LL | let [&(mut x)] = &mut [&0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:93:11 + | +LL | let [&&mut x] = &[&mut 0]; + | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +help: consider removing `&mut` from the pattern + | +LL - let [&&mut x] = &[&mut 0]; +LL + let [&x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:95:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:101:11 + | +LL | let [&&mut ref x] = &[&mut 0]; + | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +help: consider removing `&mut` from the pattern + | +LL - let [&&mut ref x] = &[&mut 0]; +LL + let [&ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:108:11 + | +LL | let [&&mut mut x] = &[&mut 0]; + | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +help: consider removing `&mut` from the pattern + | +LL - let [&&mut mut x] = &[&mut 0]; +LL + let [&mut x] = &[&mut 0]; + | + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:108:16 + | +LL | let [&&mut mut x] = &[&mut 0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:111:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error: aborting due to 27 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs new file mode 100644 index 000000000000..3b27aa0f6ac4 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -0,0 +1,40 @@ +//@ edition: 2024 +//@ revisions: classic structural +//@[classic] run-pass +//! Tests for errors from binding with `ref x` under a by-ref default binding mode. These can't be +//! in the same body as tests for other errors, since they're emitted during THIR construction. +#![allow(incomplete_features)] +#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + let [&ref x] = &[&0]; + //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic)] let _: &&u32 = x; + + let [&ref x] = &[&mut 0]; + //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic)] let _: &&mut u32 = x; + + let [&ref x] = &mut [&0]; + //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic)] let _: &&u32 = x; + + let [&ref x] = &mut [&mut 0]; + //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic)] let _: &&mut u32 = x; + + let [&mut ref x] = &mut [&mut 0]; + //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic)] let _: &&mut u32 = x; + + let [&mut ref mut x] = &mut [&mut 0]; + //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic)] let _: &mut &mut u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural.stderr new file mode 100644 index 000000000000..a952f72f08e9 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural.stderr @@ -0,0 +1,74 @@ +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:11:11 + | +LL | let [&ref x] = &[&0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[&ref x] = &[&0]; + | + + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:16:11 + | +LL | let [&ref x] = &[&mut 0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[&ref x] = &[&mut 0]; + | + + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:21:11 + | +LL | let [&ref x] = &mut [&0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [&ref x] = &mut [&0]; + | ++++ + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:26:11 + | +LL | let [&ref x] = &mut [&mut 0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [&ref x] = &mut [&mut 0]; + | ++++ + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:31:15 + | +LL | let [&mut ref x] = &mut [&mut 0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [&mut ref x] = &mut [&mut 0]; + | ++++ + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:36:15 + | +LL | let [&mut ref mut x] = &mut [&mut 0]; + | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [&mut ref mut x] = &mut [&mut 0]; + | ++++ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed index 4f4941975d80..c7216d06bb71 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed @@ -30,4 +30,8 @@ pub fn main() { //~| ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut bool = a; let _: &mut bool = b; + + let &mut [x] = &mut &mut [0]; + //[classic]~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.stderr index 6c384a51fac1..42a4a8597f7a 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.stderr @@ -38,6 +38,14 @@ LL | let &(ref mut a, ref mut b) = &mut (true, false); | | | help: replace this `&` with `&mut`: `&mut` -error: aborting due to 5 previous errors +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:11 + | +LL | let &[x] = &mut &mut [0]; + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs index b29bff7603f7..138de4f8eac8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs @@ -30,4 +30,8 @@ pub fn main() { //~| ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut bool = a; let _: &mut bool = b; + + let &[x] = &mut &mut [0]; + //[classic]~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed index 4f4941975d80..24fb99ae0299 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed @@ -30,4 +30,8 @@ pub fn main() { //~| ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut bool = a; let _: &mut bool = b; + + let &[x] = &mut &mut [0]; + //[classic]~^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &u32 = x; } From c03769524bc52b0761385cab4b0a5c5e8fd96bd7 Mon Sep 17 00:00:00 2001 From: dianne Date: Sat, 4 Jan 2025 22:01:01 -0800 Subject: [PATCH 073/342] "structural" ruleset: account for dbm mutability cap in Deref(EatInner) rules --- compiler/rustc_hir_typeck/src/pat.rs | 7 +- .../borrowck-errors.classic.stderr | 2 +- .../experimental/borrowck-errors.rs | 4 +- .../pattern-errors.classic.stderr | 8 +- .../experimental/pattern-errors.rs | 6 ++ .../pattern-errors.structural.stderr | 98 ++++++++++++++++++- 6 files changed, 112 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index cf9921312d52..1cbeb6d1fd00 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2344,7 +2344,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { InheritedRefMatchRule::EatInner => { if let ty::Ref(_, _, r_mutbl) = *expected.kind() { // Match against the reference type; don't consume the inherited ref. - pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl); + // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref + // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing + // ruleset with Rule 4 but not Rule 3, we'll need to check that here. + debug_assert!(self.downgrade_mut_inside_shared()); + let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl()); + pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap); } else { // The expected type isn't a reference, so match against the inherited ref. if pat_mutbl > inh_mut { diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr index 65b03f7a6109..d72539eeeb2f 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr @@ -46,7 +46,7 @@ LL | let [&ref x] = &[&mut 0]; | +++ error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:31:16 + --> $DIR/borrowck-errors.rs:30:16 | LL | let [&x] = &mut [&mut 0]; | - ^^^^^^^^^^^^^ cannot move out of here diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs index 7645c145b652..60f3a1dc18c4 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -27,8 +27,8 @@ pub fn main() { //[classic]~^ ERROR: cannot move out of type let _: &u32 = x; - #[cfg(classic)] // TODO: this should pass on `structural` but doesn't - let [&x] = &mut [&mut 0]; //[classic]~ ERROR: cannot move out of type + let [&x] = &mut [&mut 0]; + //[classic]~^ ERROR: cannot move out of type let _: &u32 = x; let [&mut x] = &mut [&mut 0]; diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr index fbb9a35443a2..939eb4ea430e 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr @@ -83,7 +83,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:119:10 + --> $DIR/pattern-errors.rs:125:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let [&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:124:10 + --> $DIR/pattern-errors.rs:130:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:129:10 + --> $DIR/pattern-errors.rs:135:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -119,7 +119,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:134:10 + --> $DIR/pattern-errors.rs:140:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index b5e8bd112aa8..90f77df871dd 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -96,13 +96,16 @@ fn structural_errors_2() { //[structural]~^ ERROR: mismatched types let [&&mut x] = &mut [&mut 0]; + //[structural]~^ ERROR: mismatched types let _: u32 = x; + //[structural]~^ ERROR: mismatched types let [&&mut ref x] = &[&mut 0]; //[structural]~^ ERROR: mismatched types let _: &u32 = x; let [&&mut ref x] = &mut [&mut 0]; + //[structural]~^ ERROR: mismatched types let _: &u32 = x; let [&&mut mut x] = &[&mut 0]; @@ -112,7 +115,10 @@ fn structural_errors_2() { //[structural]~^ ERROR: mismatched types let [&&mut mut x] = &mut [&mut 0]; + //[structural]~^ ERROR: binding cannot be both mutable and by-reference + //[structural]~| ERROR: mismatched types let _: u32 = x; + //[structural]~^ ERROR: mismatched types } fn classic_errors_0() { diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr index 4eabb8b1b0a3..ed7e09c937a6 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr @@ -342,7 +342,38 @@ LL | let _: u32 = *x; | + error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:101:11 + --> $DIR/pattern-errors.rs:98:11 + | +LL | let [&&mut x] = &mut [&mut 0]; + | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +help: consider removing `&mut` from the pattern + | +LL - let [&&mut x] = &mut [&mut 0]; +LL + let [&x] = &mut [&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:100:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:103:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -358,7 +389,23 @@ LL + let [&ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:108:11 + --> $DIR/pattern-errors.rs:107:11 + | +LL | let [&&mut ref x] = &mut [&mut 0]; + | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +help: consider removing `&mut` from the pattern + | +LL - let [&&mut ref x] = &mut [&mut 0]; +LL + let [&ref x] = &mut [&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:111:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -374,7 +421,7 @@ LL + let [&mut x] = &[&mut 0]; | error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:108:16 + --> $DIR/pattern-errors.rs:111:16 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^ @@ -384,7 +431,7 @@ LL | let [&&mut mut x] = &[&mut 0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:111:18 + --> $DIR/pattern-errors.rs:114:18 | LL | let _: u32 = x; | --- ^ expected `u32`, found `&_` @@ -398,7 +445,48 @@ help: consider dereferencing the borrow LL | let _: u32 = *x; | + -error: aborting due to 27 previous errors +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:117:11 + | +LL | let [&&mut mut x] = &mut [&mut 0]; + | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +help: consider removing `&mut` from the pattern + | +LL - let [&&mut mut x] = &mut [&mut 0]; +LL + let [&mut x] = &mut [&mut 0]; + | + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:117:16 + | +LL | let [&&mut mut x] = &mut [&mut 0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:120:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `&_` + | | + | expected due to this + | + = note: expected type `u32` + found reference `&_` +help: consider dereferencing the borrow + | +LL | let _: u32 = *x; + | + + +error: aborting due to 33 previous errors Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. From f8315ae3b586fcd476e3dafd85b5891ddc70cc33 Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 5 Jan 2025 19:42:26 -0800 Subject: [PATCH 074/342] "structural" ruleset: use the "classic" ruleset's diagnostic and fallback for inherited ref mutability mismatches I think the diagnostic could use some work, but it's more helpful than the alternative. The previous error was misleading, since it ignored the inherited reference altogether. --- compiler/rustc_hir_typeck/src/pat.rs | 58 +-- .../pattern-errors.classic.stderr | 8 +- .../experimental/pattern-errors.rs | 30 +- .../pattern-errors.structural.stderr | 437 ++++-------------- 4 files changed, 151 insertions(+), 382 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 1cbeb6d1fd00..ba5ba7a74527 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2318,22 +2318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E // but not Rule 5, we'll need to check that here. debug_assert!(ref_pat_matches_mut_ref); - let err_msg = "mismatched types"; - let err = if let Some(span) = pat_prefix_span { - let mut err = self.dcx().struct_span_err(span, err_msg); - err.code(E0308); - err.note("cannot match inherited `&` with `&mut` pattern"); - err.span_suggestion_verbose( - span, - "replace this `&mut` pattern with `&`", - "&", - Applicability::MachineApplicable, - ); - err - } else { - self.dcx().struct_span_err(pat.span, err_msg) - }; - err.emit(); + self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span); } pat_info.binding_mode = ByRef::No; @@ -2353,22 +2338,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // The expected type isn't a reference, so match against the inherited ref. if pat_mutbl > inh_mut { - // We can't match an inherited shared reference with `&mut`. This will - // be a type error later, since we're matching a reference pattern - // against a non-reference type. + // We can't match an inherited shared reference with `&mut`. // NB: This assumes that `&` patterns can match against mutable // references (RFC 3627, Rule 5). If we implement a pattern typing // ruleset with Rule 4 but not Rule 5, we'll need to check that here. debug_assert!(ref_pat_matches_mut_ref); - } else { - pat_info.binding_mode = ByRef::No; - self.typeck_results - .borrow_mut() - .skipped_ref_pats_mut() - .insert(pat.hir_id); - self.check_pat(inner, expected, pat_info); - return expected; + self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span); } + + pat_info.binding_mode = ByRef::No; + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; } } InheritedRefMatchRule::EatBoth => { @@ -2442,6 +2423,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_ref(self.tcx, region, ty, mutbl) } + fn error_inherited_ref_mutability_mismatch( + &self, + pat: &'tcx Pat<'tcx>, + pat_prefix_span: Option, + ) -> ErrorGuaranteed { + let err_msg = "mismatched types"; + let err = if let Some(span) = pat_prefix_span { + let mut err = self.dcx().struct_span_err(span, err_msg); + err.code(E0308); + err.note("cannot match inherited `&` with `&mut` pattern"); + err.span_suggestion_verbose( + span, + "replace this `&mut` pattern with `&`", + "&", + Applicability::MachineApplicable, + ); + err + } else { + self.dcx().struct_span_err(pat.span, err_msg) + }; + err.emit() + } + fn try_resolve_slice_ty_to_array_ty( &self, before: &'tcx [Pat<'tcx>], diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr index 939eb4ea430e..6993e724be2c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr @@ -83,7 +83,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:125:10 + --> $DIR/pattern-errors.rs:123:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let [&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:130:10 + --> $DIR/pattern-errors.rs:128:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:135:10 + --> $DIR/pattern-errors.rs:133:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -119,7 +119,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:140:10 + --> $DIR/pattern-errors.rs:138:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index 90f77df871dd..bbaa717e8bdd 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -45,37 +45,36 @@ pub fn main() { } } -// TODO: these should be mutability mismatches on `structural` fn structural_errors_0() { let &[&mut x] = &&mut [0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; - //[structural]~^ ERROR: mismatched types let &[&mut x] = &mut &mut [0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; - //[structural]~^ ERROR: mismatched types let &[&mut ref x] = &&mut [0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let &[&mut ref x] = &mut &mut [0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let &[&mut mut x] = &&mut [0]; //[structural]~^ ERROR: mismatched types - //[structural]~| ERROR: binding cannot be both mutable and by-reference + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; - //[structural]~^ ERROR: mismatched types let &[&mut mut x] = &mut &mut [0]; //[structural]~^ ERROR: mismatched types - //[structural]~| ERROR: binding cannot be both mutable and by-reference + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; - //[structural]~^ ERROR: mismatched types } fn structural_errors_1() { @@ -88,37 +87,36 @@ fn structural_errors_1() { let _: &u32 = x; } -// TODO: these should be mutability mismatches on `structural` fn structural_errors_2() { let [&&mut x] = &[&mut 0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; - //[structural]~^ ERROR: mismatched types let [&&mut x] = &mut [&mut 0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; - //[structural]~^ ERROR: mismatched types let [&&mut ref x] = &[&mut 0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&&mut ref x] = &mut [&mut 0]; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&&mut mut x] = &[&mut 0]; - //[structural]~^ ERROR: binding cannot be both mutable and by-reference - //[structural]~| ERROR: mismatched types - let _: u32 = x; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; let [&&mut mut x] = &mut [&mut 0]; - //[structural]~^ ERROR: binding cannot be both mutable and by-reference - //[structural]~| ERROR: mismatched types - let _: u32 = x; //[structural]~^ ERROR: mismatched types + //[structural]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; } fn classic_errors_0() { diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr index ed7e09c937a6..d835a3abe374 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr @@ -33,265 +33,136 @@ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:31:23 | LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:34:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:37:29 | LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^^ ------------------------- this expression has type `&Option>>` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) { + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:40:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` + | ^^^^^ | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:43:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` + | ^^^^^ | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:50:11 + --> $DIR/pattern-errors.rs:49:11 | LL | let &[&mut x] = &&mut [0]; - | ^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:50:11 - | -LL | let &[&mut x] = &&mut [0]; - | ^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - let &[&mut x] = &&mut [0]; -LL + let &[x] = &&mut [0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let &[&x] = &&mut [0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:52:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:55:11 + --> $DIR/pattern-errors.rs:54:11 | LL | let &[&mut x] = &mut &mut [0]; - | ^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:55:11 - | -LL | let &[&mut x] = &mut &mut [0]; - | ^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - let &[&mut x] = &mut &mut [0]; -LL + let &[x] = &mut &mut [0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let &[&x] = &mut &mut [0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:57:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:60:11 + --> $DIR/pattern-errors.rs:59:11 | LL | let &[&mut ref x] = &&mut [0]; - | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:60:11 - | -LL | let &[&mut ref x] = &&mut [0]; - | ^^^^^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - let &[&mut ref x] = &&mut [0]; -LL + let &[ref x] = &&mut [0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let &[&ref x] = &&mut [0]; + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:64:11 | LL | let &[&mut ref x] = &mut &mut [0]; - | ^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:64:11 - | -LL | let &[&mut ref x] = &mut &mut [0]; - | ^^^^^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - let &[&mut ref x] = &mut &mut [0]; -LL + let &[ref x] = &mut &mut [0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let &[&ref x] = &mut &mut [0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:68:11 + --> $DIR/pattern-errors.rs:69:11 | LL | let &[&mut mut x] = &&mut [0]; - | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:68:11 + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | -LL | let &[&mut mut x] = &&mut [0]; - | ^^^^^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - let &[&mut mut x] = &&mut [0]; -LL + let &[mut x] = &&mut [0]; - | - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:68:16 - | -LL | let &[&mut mut x] = &&mut [0]; - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:71:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + +LL | let &[&mut x] = &&mut [0]; + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:74:11 | LL | let &[&mut mut x] = &mut &mut [0]; - | ^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:74:11 - | -LL | let &[&mut mut x] = &mut &mut [0]; - | ^^^^^^^^^^ -help: consider removing `&mut` from the pattern - | -LL - let &[&mut mut x] = &mut &mut [0]; -LL + let &[mut x] = &mut &mut [0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let &[&mut x] = &mut &mut [0]; + | ~ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:74:16 - | -LL | let &[&mut mut x] = &mut &mut [0]; - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:77:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:82:12 + --> $DIR/pattern-errors.rs:81:12 | LL | let [&(mut x)] = &[&0]; | ^^^^ @@ -301,7 +172,7 @@ LL | let [&(mut x)] = &[&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:86:12 + --> $DIR/pattern-errors.rs:85:12 | LL | let [&(mut x)] = &mut [&0]; | ^^^^ @@ -311,182 +182,78 @@ LL | let [&(mut x)] = &mut [&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:93:11 + --> $DIR/pattern-errors.rs:91:11 | LL | let [&&mut x] = &[&mut 0]; - | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -help: consider removing `&mut` from the pattern - | -LL - let [&&mut x] = &[&mut 0]; -LL + let [&x] = &[&mut 0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let [&&x] = &[&mut 0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:95:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:98:11 + --> $DIR/pattern-errors.rs:96:11 | LL | let [&&mut x] = &mut [&mut 0]; - | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -help: consider removing `&mut` from the pattern - | -LL - let [&&mut x] = &mut [&mut 0]; -LL + let [&x] = &mut [&mut 0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let [&&x] = &mut [&mut 0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:100:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:103:11 + --> $DIR/pattern-errors.rs:101:11 | LL | let [&&mut ref x] = &[&mut 0]; - | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -help: consider removing `&mut` from the pattern - | -LL - let [&&mut ref x] = &[&mut 0]; -LL + let [&ref x] = &[&mut 0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let [&&ref x] = &[&mut 0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:107:11 + --> $DIR/pattern-errors.rs:106:11 | LL | let [&&mut ref x] = &mut [&mut 0]; - | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -help: consider removing `&mut` from the pattern - | -LL - let [&&mut ref x] = &mut [&mut 0]; -LL + let [&ref x] = &mut [&mut 0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let [&&ref x] = &mut [&mut 0]; + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:111:11 | LL | let [&&mut mut x] = &[&mut 0]; - | ^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -help: consider removing `&mut` from the pattern + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | -LL - let [&&mut mut x] = &[&mut 0]; -LL + let [&mut x] = &[&mut 0]; - | - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:111:16 - | -LL | let [&&mut mut x] = &[&mut 0]; - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +LL | let [&&mut x] = &[&mut 0]; + | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:114:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:117:11 + --> $DIR/pattern-errors.rs:116:11 | LL | let [&&mut mut x] = &mut [&mut 0]; - | ^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` - | | - | expected integer, found `&mut _` + | ^^^^^ | - = note: expected type `{integer}` - found mutable reference `&mut _` -help: consider removing `&mut` from the pattern - | -LL - let [&&mut mut x] = &mut [&mut 0]; -LL + let [&mut x] = &mut [&mut 0]; + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | +LL | let [&&mut x] = &mut [&mut 0]; + | ~ -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:117:16 - | -LL | let [&&mut mut x] = &mut [&mut 0]; - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:120:18 - | -LL | let _: u32 = x; - | --- ^ expected `u32`, found `&_` - | | - | expected due to this - | - = note: expected type `u32` - found reference `&_` -help: consider dereferencing the borrow - | -LL | let _: u32 = *x; - | + - -error: aborting due to 33 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. From 586ff158a25f421983be7b9f41437a73e56ef3cc Mon Sep 17 00:00:00 2001 From: dianne Date: Mon, 13 Jan 2025 00:07:16 -0800 Subject: [PATCH 075/342] "structural" ruleset: match against the inherited ref when a reference pattern doesn't match the mutability of an inner reference This is the `Deref(EatInner, FallbackToOuter)` rule in Typing Rust Patterns. --- compiler/rustc_hir_typeck/src/pat.rs | 13 +++- .../pattern-errors.classic.stderr | 22 +++--- .../experimental/pattern-errors.rs | 5 -- .../pattern-errors.structural.stderr | 71 +++++++------------ .../experimental/well-typed-edition-2024.rs | 25 ++++++- 5 files changed, 72 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ba5ba7a74527..992ab8cefc0f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2327,8 +2327,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return expected; } InheritedRefMatchRule::EatInner => { - if let ty::Ref(_, _, r_mutbl) = *expected.kind() { + if let ty::Ref(_, _, r_mutbl) = *expected.kind() + && pat_mutbl <= r_mutbl + { // Match against the reference type; don't consume the inherited ref. + // NB: The check for compatible pattern and ref type mutability assumes that + // `&` patterns can match against mutable references (RFC 3627, Rule 5). If + // we implement a pattern typing ruleset with Rule 4 (including the fallback + // to matching the inherited ref when the inner ref can't match) but not + // Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing // ruleset with Rule 4 but not Rule 3, we'll need to check that here. @@ -2336,7 +2344,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl()); pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap); } else { - // The expected type isn't a reference, so match against the inherited ref. + // The reference pattern can't match against the expected type, so try + // matching against the inherited ref instead. if pat_mutbl > inh_mut { // We can't match an inherited shared reference with `&mut`. // NB: This assumes that `&` patterns can match against mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr index 6993e724be2c..45ee489c5206 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:15:17 + --> $DIR/pattern-errors.rs:10:17 | LL | if let Some(&mut x) = &Some(&mut 0) { | ^^^^^ @@ -11,7 +11,7 @@ LL | if let Some(&x) = &Some(&mut 0) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:19:17 + --> $DIR/pattern-errors.rs:14:17 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^^^^ @@ -23,7 +23,7 @@ LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:23:22 + --> $DIR/pattern-errors.rs:18:22 | LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { | ^^^^^ @@ -35,7 +35,7 @@ LL | if let Some(Some(&x)) = &Some(Some(&mut 0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:28:17 + --> $DIR/pattern-errors.rs:23:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -47,7 +47,7 @@ LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:34:23 + --> $DIR/pattern-errors.rs:29:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -59,7 +59,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:40:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -71,7 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:43:17 + --> $DIR/pattern-errors.rs:38:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -83,7 +83,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:123:10 + --> $DIR/pattern-errors.rs:118:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let [&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:10 + --> $DIR/pattern-errors.rs:123:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:133:10 + --> $DIR/pattern-errors.rs:128:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -119,7 +119,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:138:10 + --> $DIR/pattern-errors.rs:133:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index bbaa717e8bdd..1e170b7d3f8e 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -7,11 +7,6 @@ #![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { - if let Some(&mut x) = &mut Some(&0) { - //[structural]~^ ERROR: mismatched types - let _: &u32 = x; - } - if let Some(&mut x) = &Some(&mut 0) { //[classic]~^ ERROR: mismatched types let _: &u32 = x; diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr index d835a3abe374..3396d4148197 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr @@ -1,36 +1,17 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:10:17 + --> $DIR/pattern-errors.rs:23:17 | -LL | if let Some(&mut x) = &mut Some(&0) { - | ^^^^^^ ------------- this expression has type `&mut Option<&{integer}>` - | | - | types differ in mutability +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^ | - = note: expected reference `&{integer}` - found mutable reference `&mut _` -note: to declare a mutable binding use: `mut x` - --> $DIR/pattern-errors.rs:10:17 + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` | -LL | if let Some(&mut x) = &mut Some(&0) { - | ^^^^^^ -help: consider removing `&mut` from the pattern - | -LL | if let Some(x) = &mut Some(&0) { +LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:28:17 - | -LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { - | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | types differ in mutability - | - = note: expected reference `&Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:31:23 + --> $DIR/pattern-errors.rs:26:23 | LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { | ^^^^^ @@ -42,7 +23,7 @@ LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:34:23 + --> $DIR/pattern-errors.rs:29:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -54,7 +35,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:37:29 + --> $DIR/pattern-errors.rs:32:29 | LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { | ^^^^^ @@ -66,7 +47,7 @@ LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:40:17 + --> $DIR/pattern-errors.rs:35:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -78,7 +59,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:43:17 + --> $DIR/pattern-errors.rs:38:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -90,7 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:49:11 + --> $DIR/pattern-errors.rs:44:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^ @@ -102,7 +83,7 @@ LL | let &[&x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:54:11 + --> $DIR/pattern-errors.rs:49:11 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^ @@ -114,7 +95,7 @@ LL | let &[&x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:59:11 + --> $DIR/pattern-errors.rs:54:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^ @@ -126,7 +107,7 @@ LL | let &[&ref x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:59:11 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^ @@ -138,7 +119,7 @@ LL | let &[&ref x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:69:11 + --> $DIR/pattern-errors.rs:64:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^ @@ -150,7 +131,7 @@ LL | let &[&mut x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:74:11 + --> $DIR/pattern-errors.rs:69:11 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^ @@ -162,7 +143,7 @@ LL | let &[&mut x] = &mut &mut [0]; | ~ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:81:12 + --> $DIR/pattern-errors.rs:76:12 | LL | let [&(mut x)] = &[&0]; | ^^^^ @@ -172,7 +153,7 @@ LL | let [&(mut x)] = &[&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:85:12 + --> $DIR/pattern-errors.rs:80:12 | LL | let [&(mut x)] = &mut [&0]; | ^^^^ @@ -182,7 +163,7 @@ LL | let [&(mut x)] = &mut [&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:91:11 + --> $DIR/pattern-errors.rs:86:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^ @@ -194,7 +175,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:96:11 + --> $DIR/pattern-errors.rs:91:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^ @@ -206,7 +187,7 @@ LL | let [&&x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:101:11 + --> $DIR/pattern-errors.rs:96:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^ @@ -218,7 +199,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:106:11 + --> $DIR/pattern-errors.rs:101:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^ @@ -230,7 +211,7 @@ LL | let [&&ref x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:111:11 + --> $DIR/pattern-errors.rs:106:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^ @@ -242,7 +223,7 @@ LL | let [&&mut x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:116:11 + --> $DIR/pattern-errors.rs:111:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^ @@ -253,7 +234,7 @@ help: replace this `&mut` pattern with `&` LL | let [&&mut x] = &mut [&mut 0]; | ~ -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index 28e008a779b1..1db7abc473e7 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -3,7 +3,7 @@ //@ run-pass //! Test cases for well-typed patterns in edition 2024. These are in their own file to ensure we //! pass both HIR typeck and MIR borrowck, as we may skip the latter if grouped with failing tests. -#![allow(incomplete_features)] +#![allow(incomplete_features, unused_mut)] #![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] #![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] @@ -53,4 +53,27 @@ pub fn main() { if let Some(&Some(x)) = &mut Some(Some(0)) { let _: u32 = x; } + + // Tests for eat-inner rulesets matching on the outer reference if matching on the inner + // reference causes a mutability mismatch, i.e. `Deref(EatInner, FallbackToOuter)`: + let [&mut x] = &mut [&0]; + let _: &u32 = x; + + let [&mut ref x] = &mut [&0]; + let _: &&u32 = x; + + let [&mut ref mut x] = &mut [&0]; + let _: &mut &u32 = x; + + let [&mut mut x] = &mut [&0]; + let _: &u32 = x; + + let [&mut &x] = &mut [&0]; + let _: u32 = x; + + let [&mut &ref x] = &mut [&0]; + let _: &u32 = x; + + let [&mut &(mut x)] = &mut [&0]; + let _: u32 = x; } From 3f9b198dcbb09f189d6d2781401c526077f19fbe Mon Sep 17 00:00:00 2001 From: dianne Date: Wed, 15 Jan 2025 01:21:35 -0800 Subject: [PATCH 076/342] rename tests' revisions to allow testing multiple editions --- ...err => borrowck-errors.classic2024.stderr} | 0 .../experimental/borrowck-errors.rs | 16 ++-- ... => borrowck-errors.structural2024.stderr} | 0 ...tably-deref-shared-ref.classic2024.stderr} | 0 .../cannot-mutably-deref-shared-ref.rs | 6 +- ...ly-deref-shared-ref.structural2024.stderr} | 0 ....stderr => mut-ref-mut.classic2024.stderr} | 0 .../experimental/mut-ref-mut.rs | 6 +- ...derr => mut-ref-mut.structural2024.stderr} | 0 ...derr => pattern-errors.classic2024.stderr} | 0 .../experimental/pattern-errors.rs | 84 +++++++++---------- ...r => pattern-errors.structural2024.stderr} | 0 .../ref-binding-on-inh-ref-errors.rs | 44 +++++----- ...g-on-inh-ref-errors.structural2024.stderr} | 0 ...t-inside-shared-ref-pat.classic2024.fixed} | 8 +- ...-inside-shared-ref-pat.classic2024.stderr} | 0 .../ref-mut-inside-shared-ref-pat.rs | 8 +- ...nside-shared-ref-pat.structural2024.fixed} | 8 +- ...side-shared-ref-pat.structural2024.stderr} | 0 .../experimental/well-typed-edition-2024.rs | 6 +- 20 files changed, 93 insertions(+), 93 deletions(-) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{borrowck-errors.classic.stderr => borrowck-errors.classic2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{borrowck-errors.structural.stderr => borrowck-errors.structural2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{cannot-mutably-deref-shared-ref.classic.stderr => cannot-mutably-deref-shared-ref.classic2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{cannot-mutably-deref-shared-ref.structural.stderr => cannot-mutably-deref-shared-ref.structural2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{mut-ref-mut.classic.stderr => mut-ref-mut.classic2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{mut-ref-mut.structural.stderr => mut-ref-mut.structural2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{pattern-errors.classic.stderr => pattern-errors.classic2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{pattern-errors.structural.stderr => pattern-errors.structural2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{ref-binding-on-inh-ref-errors.structural.stderr => ref-binding-on-inh-ref-errors.structural2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{ref-mut-inside-shared-ref-pat.classic.fixed => ref-mut-inside-shared-ref-pat.classic2024.fixed} (79%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{ref-mut-inside-shared-ref-pat.classic.stderr => ref-mut-inside-shared-ref-pat.classic2024.stderr} (100%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{ref-mut-inside-shared-ref-pat.structural.fixed => ref-mut-inside-shared-ref-pat.structural2024.fixed} (79%) rename tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/{ref-mut-inside-shared-ref-pat.structural.stderr => ref-mut-inside-shared-ref-pat.structural2024.stderr} (100%) diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs index 60f3a1dc18c4..9107d3ebf6ac 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -1,9 +1,9 @@ //@ edition: 2024 -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Tests for pattern errors not handled by the pattern typing rules, but by borrowck. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { if let Some(&Some(x)) = Some(&Some(&mut 0)) { @@ -15,23 +15,23 @@ pub fn main() { //~^ cannot borrow data in a `&` reference as mutable [E0596] if let &Some(Some(x)) = &Some(&mut Some(0)) { - //[classic]~^ ERROR: cannot borrow data in a `&` reference as mutable + //[classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable let _: &u32 = x; } let &[x] = &&mut [0]; - //[classic]~^ ERROR: cannot borrow data in a `&` reference as mutable + //[classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable let _: &u32 = x; let [&x] = &[&mut 0]; - //[classic]~^ ERROR: cannot move out of type + //[classic2024]~^ ERROR: cannot move out of type let _: &u32 = x; let [&x] = &mut [&mut 0]; - //[classic]~^ ERROR: cannot move out of type + //[classic2024]~^ ERROR: cannot move out of type let _: &u32 = x; let [&mut x] = &mut [&mut 0]; - //[classic]~^ ERROR: cannot move out of type + //[classic2024]~^ ERROR: cannot move out of type let _: &mut u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs index e22bd1f8f6c9..a493b672c929 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs @@ -1,9 +1,9 @@ //@ edition: 2024 -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Test that `&mut` patterns don't match shared reference types under new typing rules in Rust 2024 #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { let &mut _ = &&0; diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs index 786587984ba4..c2fb3f287092 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs @@ -1,9 +1,9 @@ //@ edition: 2024 -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Test diagnostics for binding with `mut` when the default binding mode is by-ref. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { struct Foo(u8); diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index 1e170b7d3f8e..22ff0708d93a 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -1,22 +1,22 @@ //@ edition: 2024 -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Test cases for poorly-typed patterns in edition 2024 which are caught by HIR typeck. These must //! be separate from cases caught by MIR borrowck or the latter errors may not be emitted. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { if let Some(&mut x) = &Some(&mut 0) { - //[classic]~^ ERROR: mismatched types + //[classic2024]~^ ERROR: mismatched types let _: &u32 = x; } if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { - //[classic]~^ ERROR: mismatched types + //[classic2024]~^ ERROR: mismatched types let _: u32 = x; } if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { - //[classic]~^ ERROR: mismatched types + //[classic2024]~^ ERROR: mismatched types let _: &u32 = x; } @@ -24,13 +24,13 @@ pub fn main() { //~^ ERROR: mismatched types } if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - //[structural]~^ ERROR: mismatched types + //[structural2024]~^ ERROR: mismatched types } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types } if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - //[structural]~^ ERROR: mismatched types + //[structural2024]~^ ERROR: mismatched types } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types @@ -42,96 +42,96 @@ pub fn main() { fn structural_errors_0() { let &[&mut x] = &&mut [0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let &[&mut x] = &mut &mut [0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let &[&mut ref x] = &&mut [0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let &[&mut ref x] = &mut &mut [0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let &[&mut mut x] = &&mut [0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let &[&mut mut x] = &mut &mut [0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } fn structural_errors_1() { let [&(mut x)] = &[&0]; - //[structural]~^ ERROR: binding cannot be both mutable and by-reference + //[structural2024]~^ ERROR: binding cannot be both mutable and by-reference let _: &u32 = x; let [&(mut x)] = &mut [&0]; - //[structural]~^ ERROR: binding cannot be both mutable and by-reference + //[structural2024]~^ ERROR: binding cannot be both mutable and by-reference let _: &u32 = x; } fn structural_errors_2() { let [&&mut x] = &[&mut 0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&&mut x] = &mut [&mut 0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&&mut ref x] = &[&mut 0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&&mut ref x] = &mut [&mut 0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&&mut mut x] = &[&mut 0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&&mut mut x] = &mut [&mut 0]; - //[structural]~^ ERROR: mismatched types - //[structural]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } fn classic_errors_0() { let [&mut x] = &[&mut 0]; - //[classic]~^ ERROR: mismatched types - //[classic]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&mut &x] = &[&mut 0]; - //[classic]~^ ERROR: mismatched types - //[classic]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&mut &ref x] = &[&mut 0]; - //[classic]~^ ERROR: mismatched types - //[classic]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&mut &(mut x)] = &[&mut 0]; - //[classic]~^ ERROR: mismatched types - //[classic]~| cannot match inherited `&` with `&mut` pattern + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs index 3b27aa0f6ac4..de06489b9f81 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -1,40 +1,40 @@ //@ edition: 2024 -//@ revisions: classic structural -//@[classic] run-pass +//@ revisions: classic2024 structural2024 +//@[classic2024] run-pass //! Tests for errors from binding with `ref x` under a by-ref default binding mode. These can't be //! in the same body as tests for other errors, since they're emitted during THIR construction. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { let [&ref x] = &[&0]; - //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic)] let _: &&u32 = x; + //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&u32 = x; let [&ref x] = &[&mut 0]; - //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic)] let _: &&mut u32 = x; + //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&mut u32 = x; let [&ref x] = &mut [&0]; - //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic)] let _: &&u32 = x; + //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&u32 = x; let [&ref x] = &mut [&mut 0]; - //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic)] let _: &&mut u32 = x; + //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&mut u32 = x; let [&mut ref x] = &mut [&mut 0]; - //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic)] let _: &&mut u32 = x; + //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&mut u32 = x; let [&mut ref mut x] = &mut [&mut 0]; - //[structural]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic)] let _: &mut &mut u32 = x; + //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &mut &mut u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed similarity index 79% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed index c7216d06bb71..82c40878f493 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed @@ -1,11 +1,11 @@ //@ edition: 2024 //@ run-rustfix -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts //! to bind by mutable reference. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { @@ -32,6 +32,6 @@ pub fn main() { let _: &mut bool = b; let &mut [x] = &mut &mut [0]; - //[classic]~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024]~^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs index 138de4f8eac8..62b9037fac4c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs @@ -1,11 +1,11 @@ //@ edition: 2024 //@ run-rustfix -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts //! to bind by mutable reference. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { @@ -32,6 +32,6 @@ pub fn main() { let _: &mut bool = b; let &[x] = &mut &mut [0]; - //[classic]~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024]~^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed similarity index 79% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed index 24fb99ae0299..32e955db12ce 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed @@ -1,11 +1,11 @@ //@ edition: 2024 //@ run-rustfix -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts //! to bind by mutable reference. #![allow(incomplete_features)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { @@ -32,6 +32,6 @@ pub fn main() { let _: &mut bool = b; let &[x] = &mut &mut [0]; - //[classic]~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024]~^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr similarity index 100% rename from tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index 1db7abc473e7..8a2593d0e3ab 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -1,11 +1,11 @@ //@ edition: 2024 -//@ revisions: classic structural +//@ revisions: classic2024 structural2024 //@ run-pass //! Test cases for well-typed patterns in edition 2024. These are in their own file to ensure we //! pass both HIR typeck and MIR borrowck, as we may skip the latter if grouped with failing tests. #![allow(incomplete_features, unused_mut)] -#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { if let Some(Some(&x)) = &Some(&Some(0)) { From 7f162fa9af5141c69b9a8fa79f108ad1cc60d35b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 20 Jan 2025 15:22:43 +0100 Subject: [PATCH 077/342] `short_circuit_statement`: handle macros and parenthesis better - The lint no longer triggers if the expression comes from macro expansion - Parenthesis are now removed inside the generated block if they are no longer necessary. --- clippy_lints/src/misc.rs | 19 +++++++------ tests/ui/short_circuit_statement.fixed | 36 +++++++++++++++++++++++++ tests/ui/short_circuit_statement.rs | 36 +++++++++++++++++++++++++ tests/ui/short_circuit_statement.stderr | 30 ++++++++++++++++++--- 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b856c929cf67..7b79b4f61d05 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -216,9 +216,10 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { ); }; if let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Binary(ref binop, a, b) = expr.kind - && (binop.node == BinOpKind::And || binop.node == BinOpKind::Or) - && let Some(sugg) = Sugg::hir_opt(cx, a) + && let ExprKind::Binary(binop, a, b) = &expr.kind + && matches!(binop.node, BinOpKind::And | BinOpKind::Or) + && !stmt.span.from_expansion() + && expr.span.eq_ctxt(stmt.span) { span_lint_hir_and_then( cx, @@ -227,13 +228,11 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { stmt.span, "boolean short circuit operator in statement may be clearer using an explicit test", |diag| { - let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; - diag.span_suggestion( - stmt.span, - "replace it with", - format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),), - Applicability::MachineApplicable, // snippet - ); + let mut app = Applicability::MachineApplicable; + let test = Sugg::hir_with_context(cx, a, expr.span.ctxt(), "_", &mut app); + let test = if binop.node == BinOpKind::Or { !test } else { test }; + let then = Sugg::hir_with_context(cx, b, expr.span.ctxt(), "_", &mut app); + diag.span_suggestion(stmt.span, "replace it with", format!("if {test} {{ {then}; }}"), app); }, ); }; diff --git a/tests/ui/short_circuit_statement.fixed b/tests/ui/short_circuit_statement.fixed index a9930ef4dbb6..a2bf07ac6052 100644 --- a/tests/ui/short_circuit_statement.fixed +++ b/tests/ui/short_circuit_statement.fixed @@ -3,8 +3,35 @@ fn main() { if f() { g(); } + //~^ ERROR: boolean short circuit operator in statement if !f() { g(); } + //~^ ERROR: boolean short circuit operator in statement if 1 != 2 { g(); } + //~^ ERROR: boolean short circuit operator in statement + if f() || g() { H * 2; } + //~^ ERROR: boolean short circuit operator in statement + if !(f() || g()) { H * 2; } + //~^ ERROR: boolean short circuit operator in statement + + macro_rules! mac { + ($f:ident or $g:ident) => { + $f() || $g() + }; + ($f:ident and $g:ident) => { + $f() && $g() + }; + () => { + f() && g() + }; + } + + if mac!() { mac!(); } + //~^ ERROR: boolean short circuit operator in statement + if !mac!() { mac!(); } + //~^ ERROR: boolean short circuit operator in statement + + // Do not lint if the expression comes from a macro + mac!(); } fn f() -> bool { @@ -14,3 +41,12 @@ fn f() -> bool { fn g() -> bool { false } + +struct H; + +impl std::ops::Mul for H { + type Output = bool; + fn mul(self, other: u32) -> Self::Output { + true + } +} diff --git a/tests/ui/short_circuit_statement.rs b/tests/ui/short_circuit_statement.rs index 71f7c7f2abf7..bdba546ad8f6 100644 --- a/tests/ui/short_circuit_statement.rs +++ b/tests/ui/short_circuit_statement.rs @@ -3,8 +3,35 @@ fn main() { f() && g(); + //~^ ERROR: boolean short circuit operator in statement f() || g(); + //~^ ERROR: boolean short circuit operator in statement 1 == 2 || g(); + //~^ ERROR: boolean short circuit operator in statement + (f() || g()) && (H * 2); + //~^ ERROR: boolean short circuit operator in statement + (f() || g()) || (H * 2); + //~^ ERROR: boolean short circuit operator in statement + + macro_rules! mac { + ($f:ident or $g:ident) => { + $f() || $g() + }; + ($f:ident and $g:ident) => { + $f() && $g() + }; + () => { + f() && g() + }; + } + + mac!() && mac!(); + //~^ ERROR: boolean short circuit operator in statement + mac!() || mac!(); + //~^ ERROR: boolean short circuit operator in statement + + // Do not lint if the expression comes from a macro + mac!(); } fn f() -> bool { @@ -14,3 +41,12 @@ fn f() -> bool { fn g() -> bool { false } + +struct H; + +impl std::ops::Mul for H { + type Output = bool; + fn mul(self, other: u32) -> Self::Output { + true + } +} diff --git a/tests/ui/short_circuit_statement.stderr b/tests/ui/short_circuit_statement.stderr index e7a8f2ca60ca..ecf6676405b4 100644 --- a/tests/ui/short_circuit_statement.stderr +++ b/tests/ui/short_circuit_statement.stderr @@ -8,16 +8,40 @@ LL | f() && g(); = help: to override `-D warnings` add `#[allow(clippy::short_circuit_statement)]` error: boolean short circuit operator in statement may be clearer using an explicit test - --> tests/ui/short_circuit_statement.rs:6:5 + --> tests/ui/short_circuit_statement.rs:7:5 | LL | f() || g(); | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }` error: boolean short circuit operator in statement may be clearer using an explicit test - --> tests/ui/short_circuit_statement.rs:7:5 + --> tests/ui/short_circuit_statement.rs:9:5 | LL | 1 == 2 || g(); | ^^^^^^^^^^^^^^ help: replace it with: `if 1 != 2 { g(); }` -error: aborting due to 3 previous errors +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:11:5 + | +LL | (f() || g()) && (H * 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `if f() || g() { H * 2; }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:13:5 + | +LL | (f() || g()) || (H * 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `if !(f() || g()) { H * 2; }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:28:5 + | +LL | mac!() && mac!(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `if mac!() { mac!(); }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:30:5 + | +LL | mac!() || mac!(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `if !mac!() { mac!(); }` + +error: aborting due to 7 previous errors From 2fd629654fb08884a5f00f1589c3dd172eee2b5a Mon Sep 17 00:00:00 2001 From: LemonJ <1632798336@qq.com> Date: Tue, 21 Jan 2025 16:25:56 +0800 Subject: [PATCH 078/342] add missing allocator safety in alloc crate --- library/alloc/src/boxed.rs | 5 ++++- library/alloc/src/sync.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1b5e44a91346..8b38e6fc259a 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1115,6 +1115,8 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// + /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1170,7 +1172,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by `alloc` + /// The raw pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -1225,6 +1227,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The non-null pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 8eee7cff2080..431e19e6ef1d 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2740,7 +2740,7 @@ impl Weak { /// # Safety /// /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference. + /// weak reference, and must point to a block of memory allocated by global allocator. /// /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this /// takes ownership of one weak reference currently represented as a raw pointer (the weak From 54d1d318d3f89eb66029252139c27e19b5e808a0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 10:05:32 +0100 Subject: [PATCH 079/342] Cleanup `Name` string rendering --- .../crates/hir-def/src/import_map.rs | 15 +-- .../hir-def/src/nameres/mod_resolution.rs | 19 +--- .../crates/hir-expand/src/mod_path.rs | 49 +++------ .../crates/hir-expand/src/name.rs | 100 ++++++------------ .../hir-ty/src/diagnostics/decl_check.rs | 3 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../src/handlers/convert_bool_then.rs | 2 +- .../src/handlers/convert_closure_to_fn.rs | 8 +- .../extract_struct_from_enum_variant.rs | 2 +- .../src/handlers/fix_visibility.rs | 2 +- .../src/handlers/move_from_mod_rs.rs | 4 +- .../src/handlers/move_module_to_file.rs | 2 +- .../src/handlers/move_to_mod_rs.rs | 4 +- .../ide-assists/src/handlers/qualify_path.rs | 2 +- .../src/handlers/reorder_fields.rs | 2 +- .../src/handlers/reorder_impl_items.rs | 2 +- .../src/completions/flyimport.rs | 4 +- .../src/completions/item_list/trait_impl.rs | 2 +- .../ide-completion/src/completions/mod_.rs | 6 +- .../ide-completion/src/context/analysis.rs | 4 +- .../crates/ide-completion/src/render.rs | 10 +- .../ide-completion/src/render/const_.rs | 6 +- .../ide-completion/src/render/function.rs | 9 +- .../ide-completion/src/render/literal.rs | 2 +- .../ide-completion/src/render/macro_.rs | 14 ++- .../ide-completion/src/render/pattern.rs | 16 ++- .../ide-completion/src/render/type_alias.rs | 7 +- .../src/render/union_literal.rs | 8 +- .../rust-analyzer/crates/ide-db/src/defs.rs | 2 +- .../crates/ide-db/src/famous_defs.rs | 5 +- .../rust-analyzer/crates/ide-db/src/rename.rs | 9 +- .../rust-analyzer/crates/ide-db/src/search.rs | 10 +- .../crates/ide-db/src/ty_filter.rs | 2 +- .../src/handlers/unlinked_file.rs | 4 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 49 ++++----- .../crates/ide/src/inlay_hints/param_name.rs | 4 +- .../rust-analyzer/crates/ide/src/rename.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 1 + 38 files changed, 145 insertions(+), 249 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ac262950f13c..a3ffdf770e81 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -10,7 +10,6 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::{format_to, TupleExt}; -use syntax::ToSmolStr; use triomphe::Arc; use crate::{ @@ -88,9 +87,9 @@ impl ImportMap { .iter() // We've only collected items, whose name cannot be tuple field so unwrapping is fine. .flat_map(|(&item, (info, _))| { - info.iter().enumerate().map(move |(idx, info)| { - (item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32) - }) + info.iter() + .enumerate() + .map(move |(idx, info)| (item, info.name.as_str(), idx as u32)) }) .collect(); importables.sort_by(|(_, l_info, _), (_, r_info, _)| { @@ -441,7 +440,7 @@ pub fn search_dependencies( } fn search_maps( - db: &dyn DefDatabase, + _db: &dyn DefDatabase, import_maps: &[Arc], mut stream: fst::map::Union<'_>, query: &Query, @@ -464,11 +463,7 @@ fn search_maps( .then(|| (item, &import_infos[info_idx as usize])) }) .filter(|&(_, info)| { - query.search_mode.check( - &query.query, - query.case_sensitive, - &info.name.unescaped().display(db.upcast()).to_smolstr(), - ) + query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str()) }); res.extend(iter.map(TupleExt::head)); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index ab4ffbb2c1e4..d7e4ca41cd5d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -4,7 +4,6 @@ use base_db::AnchoredPath; use hir_expand::{name::Name, HirFileIdExt}; use limit::Limit; use span::EditionedFileId; -use syntax::ToSmolStr as _; use crate::{db::DefDatabase, HirFileId}; @@ -35,7 +34,7 @@ impl ModDir { let path = match attr_path { None => { let mut path = self.dir_path.clone(); - path.push(&name.unescaped().display_no_db().to_smolstr()); + path.push(name.as_str()); path } Some(attr_path) => { @@ -66,7 +65,7 @@ impl ModDir { name: &Name, attr_path: Option<&str>, ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> { - let name = name.unescaped(); + let name = name.as_str(); let mut candidate_files = ArrayVec::<_, 2>::new(); match attr_path { @@ -74,16 +73,8 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None => { - candidate_files.push(format!( - "{}{}.rs", - self.dir_path.0, - name.display(db.upcast()) - )); - candidate_files.push(format!( - "{}{}/mod.rs", - self.dir_path.0, - name.display(db.upcast()) - )); + candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); + candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } }; @@ -97,7 +88,7 @@ impl ModDir { let dir_path = if root_dir_owner { DirPath::empty() } else { - DirPath::new(format!("{}/", name.display(db.upcast()))) + DirPath::new(format!("{}/", name)) }; if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) { return Ok(( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 89eae862bd96..f0cf7ebf479f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -23,15 +23,6 @@ pub struct ModPath { segments: SmallVec<[Name; 1]>, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnescapedModPath<'a>(&'a ModPath); - -impl<'a> UnescapedModPath<'a> { - pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - UnescapedDisplay { db, path: self } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -135,9 +126,11 @@ impl ModPath { _ => None, } } - - pub fn unescaped(&self) -> UnescapedModPath<'_> { - UnescapedModPath(self) + pub fn display_verbatim<'a>( + &'a self, + db: &'a dyn crate::db::ExpandDatabase, + ) -> impl fmt::Display + 'a { + Display { db, path: self, edition: None } } pub fn display<'a>( @@ -145,7 +138,7 @@ impl ModPath { db: &'a dyn crate::db::ExpandDatabase, edition: Edition, ) -> impl fmt::Display + 'a { - Display { db, path: self, edition } + Display { db, path: self, edition: Some(edition) } } } @@ -158,23 +151,12 @@ impl Extend for ModPath { struct Display<'a> { db: &'a dyn ExpandDatabase, path: &'a ModPath, - edition: Edition, + edition: Option, } impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition)) - } -} - -struct UnescapedDisplay<'a> { - db: &'a dyn ExpandDatabase, - path: &'a UnescapedModPath<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path.0, f, Escape::No) + display_fmt_path(self.db, self.path, f, self.edition) } } @@ -184,16 +166,11 @@ impl From for ModPath { } } -enum Escape { - No, - IfNeeded(Edition), -} - fn display_fmt_path( db: &dyn ExpandDatabase, path: &ModPath, f: &mut fmt::Formatter<'_>, - escaped: Escape, + edition: Option, ) -> fmt::Result { let mut first_segment = true; let mut add_segment = |s| -> fmt::Result { @@ -221,10 +198,10 @@ fn display_fmt_path( f.write_str("::")?; } first_segment = false; - match escaped { - Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?, - Escape::No => segment.unescaped().display(db).fmt(f)?, - } + match edition { + Some(edition) => segment.display(db, edition).fmt(f)?, + None => fmt::Display::fmt(segment.as_str(), f)?, + }; } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 9cfa3b4b32b0..848870c3a384 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -4,8 +4,8 @@ use std::fmt; use intern::{sym, Symbol}; use span::{Edition, SyntaxContextId}; -use syntax::ast; use syntax::utils::is_raw_identifier; +use syntax::{ast, format_smolstr}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are @@ -69,27 +69,8 @@ impl PartialEq for &Symbol { } } -/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnescapedName<'a>(&'a Name); - -impl<'a> UnescapedName<'a> { - pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - _ = db; - UnescapedDisplay { name: self } - } - #[doc(hidden)] - pub fn display_no_db(self) -> impl fmt::Display + 'a { - UnescapedDisplay { name: self } - } -} - impl Name { - /// Note: this is private to make creating name from random string hard. - /// Hopefully, this should allow us to integrate hygiene cleaner in the - /// future, and to switch to interned representation of names. fn new_text(text: &str) -> Name { - debug_assert!(!text.starts_with("r#")); Name { symbol: Symbol::intern(text), ctx: () } } @@ -99,12 +80,15 @@ impl Name { // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - Self::new_text(text) + match text.strip_prefix("r#") { + Some(text) => Self::new_text(text), + None => Self::new_text(text), + } } pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. - Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015)) + Self::new(text, SyntaxContextId::root(Edition::Edition2015)) } pub fn new_tuple_field(idx: usize) -> Name { @@ -131,12 +115,22 @@ impl Name { } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Self::new_text(lt.text().as_str().trim_start_matches("r#")) + let text = lt.text(); + match text.strip_prefix("'r#") { + Some(text) => Self::new_text(&format_smolstr!("'{text}")), + None => Self::new_text(text.as_str()), + } } - /// Resolve a name from the text of token. - fn resolve(raw_text: &str) -> Name { - Name::new_text(raw_text.trim_start_matches("r#")) + pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + debug_assert!(!symbol.as_str().starts_with("r#")); + _ = ctx; + Self { symbol, ctx: () } + } + + // FIXME: This needs to go once we have hygiene + pub fn new_symbol_root(sym: Symbol) -> Self { + Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015)) } /// A fake name for things missing in the source code. @@ -173,22 +167,19 @@ impl Name { self.symbol.as_str().parse().ok() } + /// Whether this name needs to be escaped in the given edition via `r#`. + pub fn needs_escape(&self, edition: Edition) -> bool { + is_raw_identifier(self.symbol.as_str(), edition) + } + /// Returns the text this name represents if it isn't a tuple field. /// /// Do not use this for user-facing text, use `display` instead to handle editions properly. + // FIXME: This should take a database argument to hide the interning pub fn as_str(&self) -> &str { self.symbol.as_str() } - // FIXME: Remove this - pub fn unescaped(&self) -> UnescapedName<'_> { - UnescapedName(self) - } - - pub fn needs_escape(&self, edition: Edition) -> bool { - is_raw_identifier(self.symbol.as_str(), edition) - } - pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -198,7 +189,7 @@ impl Name { self.display_no_db(edition) } - // FIXME: Remove this + // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) } @@ -207,24 +198,6 @@ impl Name { pub fn symbol(&self) -> &Symbol { &self.symbol } - - pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { - debug_assert!(!symbol.as_str().starts_with("r#")); - _ = ctx; - Self { symbol, ctx: () } - } - - // FIXME: This needs to go once we have hygiene - pub fn new_symbol_root(sym: Symbol) -> Self { - debug_assert!(!sym.as_str().starts_with("r#")); - Self { symbol: sym, ctx: () } - } - - // FIXME: Remove this - #[inline] - pub fn eq_ident(&self, ident: &str) -> bool { - self.as_str() == ident.trim_start_matches("r#") - } } struct Display<'a> { @@ -241,17 +214,6 @@ impl fmt::Display for Display<'_> { } } -struct UnescapedDisplay<'a> { - name: UnescapedName<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let symbol = self.name.0.symbol.as_str(); - fmt::Display::fmt(symbol, f) - } -} - pub trait AsName { fn as_name(&self) -> Name; } @@ -260,14 +222,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::resolve(&self.text()), + None => Name::new_root(&self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::resolve(&self.text()) + Name::new_root(&self.text()) } } @@ -282,7 +244,7 @@ impl AsName for ast::NameOrNameRef { impl AsName for tt::Ident { fn as_name(&self) -> Name { - Name::resolve(self.sym.as_str()) + Name::new_root(self.sym.as_str()) } } @@ -300,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_text(&self.name) + Name::new_root(&self.name) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 4991d173b9c4..774991560e9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> { .filter_map(|(pat_id, pat)| match pat { Pat::Bind { id, .. } => { let bind_name = &body.bindings[*id].name; - let mut suggested_text = - to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?; + let mut suggested_text = to_lower_snake_case(bind_name.as_str())?; if is_raw_identifier(&suggested_text, edition) { suggested_text.insert_str(0, "r#"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 2523aba53833..9283c46d0f61 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -277,7 +277,7 @@ impl CapturedItem { /// Converts the place to a name that can be inserted into source code. pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { let body = db.body(owner); - let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string(); + let mut result = body[self.place.local].name.as_str().to_owned(); for proj in &self.place.projections { match proj { ProjectionElem::Deref => {} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index a5c5b08d5b0c..eb784cd1226f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if !func.name(ctx.sema.db).eq_ident("then") { + if func.name(ctx.sema.db) != sym::then { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index bb04a43cf961..d34cf895cd90 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -343,11 +343,9 @@ fn compute_closure_type_params( let mut mentioned_names = mentioned_generic_params .iter() .filter_map(|param| match param { - hir::GenericParam::TypeParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) - } + hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()), hir::GenericParam::ConstParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) + Some(param.name(ctx.db()).as_str().to_smolstr()) } hir::GenericParam::LifetimeParam(_) => None, }) @@ -390,7 +388,7 @@ fn compute_closure_type_params( let has_name = syntax .descendants() .filter_map(ast::NameOrNameRef::cast) - .any(|name| mentioned_names.contains(&*name.text())); + .any(|name| mentioned_names.contains(name.text().trim_start_matches("r#"))); let mut has_new_params = false; if has_name { syntax diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 615b5d3f98b5..d4f2ea3bd941 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.eq_ident(variant_name.text().as_str())) + .any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#")) } fn extract_generic_params( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 7a92d8911bf8..47e4a68293f0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let (_, def) = module .scope(ctx.db(), None) .into_iter() - .find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?; + .find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?; let ScopeDef::ModuleDef(def) = def else { return None; }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 8a7a06b380f5..10915f8aafb8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 9692b7059291..bbf18e21948e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> .string_value_unescape() .is_none() => { - format_to!(buf, "{}/", name.unescaped().display(db)) + format_to!(buf, "{}/", name.as_str()) } _ => (), } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 2925e2334b44..7b38c795dc80 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 849b8a42c694..2a8465f634cf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -208,7 +208,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.eq_ident(trait_method_name.text().as_str())) + .map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#")) .unwrap_or(false) }) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index 972303c2a041..a79a82be4507 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -110,7 +110,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx)) .collect(); Some(res) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index eb1d538f8743..c3404173eafe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -122,7 +122,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, name)| (name.as_str().to_owned(), idx)) .collect(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 73313eeaa6b7..435b88de4ae6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -5,7 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxNode}; use crate::{ config::AutoImportExclusionType, @@ -444,7 +444,7 @@ fn compute_fuzzy_completion_order_key( cov_mark::hit!(certain_fuzzy_order_test); let import_name = match proposed_mod_path.segments().last() { // FIXME: nasty alloc, this is a hot path! - Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(), + Some(name) => name.as_str().to_ascii_lowercase(), None => return usize::MAX, }; match import_name.match_indices(user_input_lowercased).next() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 18629529b75b..5f0288ae9509 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -362,7 +362,7 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr(); + let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); let label = format_smolstr!("type {alias_name} ="); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index bafe32942098..cca6a22f290d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -7,7 +7,7 @@ use ide_db::{ base_db::{SourceRootDatabase, VfsPath}, FxHashSet, RootDatabase, SymbolKind, }; -use syntax::{ast, AstNode, SyntaxKind, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxKind}; use crate::{context::CompletionContext, CompletionItem, Completions}; @@ -140,9 +140,7 @@ fn directory_to_look_for_submodules( module_chain_to_containing_module_file(module, db) .into_iter() .filter_map(|module| module.name(db)) - .try_fold(base_directory, |path, name| { - path.join(&name.unescaped().display_no_db().to_smolstr()) - }) + .try_fold(base_directory, |path, name| path.join(name.as_str())) } fn module_chain_to_containing_module_file( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 562f60dbe2bb..f5a50ae81907 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1595,11 +1595,11 @@ fn pattern_context_for( }).map(|enum_| enum_.variants(sema.db)) }) }).map(|variants| variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string(); + let variant_name = variant.name(sema.db); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { - let pat_already_present = pat.syntax().to_string().contains(&variant_name); + let pat_already_present = pat.syntax().to_string().contains(variant_name.as_str()); pat_already_present.then_some(pat_already_present) }).is_some() }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 61e8114d381a..ec26e311e88f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -130,10 +130,8 @@ pub(crate) fn render_field( let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); let name = field.name(db); - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -142,7 +140,7 @@ pub(crate) fn render_field( ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), - exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), + exact_name_match: compute_exact_name_match(ctx.completion, &name), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -512,7 +510,7 @@ fn render_resolution_simple_( let mut item = CompletionItem::new( kind, ctx.source_range(), - local_name.unescaped().display(db).to_smolstr(), + local_name.as_str().to_smolstr(), ctx.completion.edition, ); item.set_relevance(ctx.completion_relevance()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 415d87c6239b..e357ab24d22d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -14,10 +14,8 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display(db, ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr()); let detail = const_.display(db, ctx.completion.edition).to_string(); let mut item = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 3b97d67169ec..317c93b10f82 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -59,13 +59,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())), + format_smolstr!("{}.{}", receiver, name.as_str()), format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)), ), - _ => ( - name.unescaped().display(db).to_smolstr(), - name.display(db, completion.edition).to_smolstr(), - ), + _ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()), }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( @@ -151,7 +148,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail) - .lookup_by(name.unescaped().display(db).to_smolstr()); + .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { add_call_parens( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index c71356e5300f..aab54ca5e014 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -75,7 +75,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = ( - qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display_verbatim(ctx.db()).to_string(), qualified_name.display(ctx.db(), completion.edition).to_string(), ); let snippet_cap = ctx.snippet_cap(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 6490171fbb48..e265e92f9794 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -46,21 +46,19 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); - let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; + let (bra, ket) = if is_fn_like { guess_macro_braces(name, docs_str) } else { ("", "") }; let needs_bang = is_fn_like && !is_use_path && !has_macro_bang; let mut item = CompletionItem::new( SymbolKind::from(macro_.kind(completion.db)), source_range, - label(&ctx, needs_bang, bra, ket, &name), + label(&ctx, needs_bang, bra, ket, &name.to_smolstr()), completion.edition, ); item.set_deprecated(ctx.is_deprecated(macro_)) @@ -71,11 +69,11 @@ fn render( match ctx.snippet_cap() { Some(cap) if needs_bang && !has_call_parens => { let snippet = format!("{escaped_name}!{bra}$0{ket}"); - let lookup = banged_name(&name); + let lookup = banged_name(name); item.insert_snippet(cap, snippet).lookup_by(lookup); } _ if needs_bang => { - item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); + item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(name)); } _ => { cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 5675dfb5c6ff..124abb17b6a1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -31,13 +31,11 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr()); let kind = strukt.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap()); - let lookup = format_literal_lookup(name.as_str(), kind); + let label = format_literal_label(name, kind, ctx.snippet_cap()); + let lookup = format_literal_lookup(name, kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; let db = ctx.db(); @@ -61,13 +59,13 @@ pub(crate) fn render_variant_pat( let (name, escaped_name) = match path { Some(path) => ( - path.unescaped().display(ctx.db()).to_string().into(), - path.display(ctx.db(), ctx.completion.edition).to_string().into(), + path.display_verbatim(ctx.db()).to_smolstr(), + path.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); let it = ( - name.unescaped().display(ctx.db()).to_smolstr(), + name.as_str().to_smolstr(), name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ); it diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 09eb19201c5b..1b952f31360c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -32,14 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( - SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]), + SmolStr::from_iter([&name.as_str().to_smolstr(), " = "]), SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]), ) } else { - ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ) + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()) }; let detail = type_alias.display(db, ctx.completion.edition).to_string(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index e053e299d903..742036265211 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -23,12 +23,12 @@ pub(crate) fn render_union_literal( let (qualified_name, escaped_qualified_name) = match path { Some(p) => ( - p.unescaped().display(ctx.db()).to_string(), - p.display(ctx.db(), ctx.completion.edition).to_string(), + p.display_verbatim(ctx.db()).to_smolstr(), + p.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => ( - name.unescaped().display(ctx.db()).to_string(), - name.display(ctx.db(), ctx.completion.edition).to_string(), + name.as_str().to_smolstr(), + name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), }; let label = format_literal_label( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 49d26dfe25c1..d12bda0816fd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -794,7 +794,7 @@ impl NameRefClass { hir::AssocItem::TypeAlias(it) => Some(it), _ => None, }) - .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) + .find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#")) { // No substitution, this can only occur in type position. return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 1dc61e3f0a8d..9b76baf48729 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -214,14 +214,15 @@ impl FamousDefs<'_, '_> { for segment in path { module = module.children(db).find_map(|child| { let name = child.name(db)?; - if name.eq_ident(segment) { + if name.as_str() == segment { Some(child) } else { None } })?; } - let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1; + let def = + module.scope(db, None).into_iter().find(|(name, _def)| name.as_str() == trait_)?.1; Some(def) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 42efbd68e33d..59914bedde43 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -263,13 +263,12 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => Some(( - format!("../{}", mod_name.unescaped().display(sema.db)), - format!("../{new_name}"), - )), + (true, _, Some(mod_name)) => { + Some((format!("../{}", mod_name.as_str()), format!("../{new_name}"))) + } // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) + Some((mod_name.as_str().to_owned(), new_name.to_owned())) } _ => None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index a75aba137be6..7fc563a42410 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -625,7 +625,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("collect_possible_aliases").entered(); let db = sema.db; - let container_name = container.name(db).unescaped().display(db).to_smolstr(); + let container_name = container.name(db).as_str().to_smolstr(); let search_scope = Definition::from(container).search_scope(db); let mut seen = FxHashSet::default(); let mut completed = FxHashSet::default(); @@ -925,12 +925,8 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - // We need to unescape the name in case it is written without "r#" in earlier - // editions of Rust where it isn't a keyword. - self.def - .name(sema.db) - .or_else(self_kw_refs) - .map(|it| it.unescaped().display(sema.db).to_smolstr()) + // We need to search without the `r#`, hence `as_str` access. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr()) } }; let name = match &name { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs index 515bc418cb46..2fdd8358637d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs @@ -26,7 +26,7 @@ impl TryEnum { _ => return None, }; TryEnum::ALL.iter().find_map(|&var| { - if enum_.name(sema.db).eq_ident(var.type_name()) { + if enum_.name(sema.db).as_str() == var.type_name() { return Some(var); } None diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 13591dfb2eeb..f3109b9bb73a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -112,7 +112,7 @@ fn fixes( // shouldn't occur _ => continue 'crates, }; - match current.children.iter().find(|(name, _)| name.eq_ident(seg)) { + match current.children.iter().find(|(name, _)| name.as_str() == seg) { Some((_, &child)) => current = &crate_def_map[child], None => continue 'crates, } @@ -161,7 +161,7 @@ fn fixes( // try finding a parent that has an inline tree from here on let mut current = module; for s in stack.iter().rev() { - match module.children.iter().find(|(name, _)| name.eq_ident(s)) { + match module.children.iter().find(|(name, _)| name.as_str() == s) { Some((_, child)) => { current = &crate_def_map[*child]; } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index bc9843f3f35a..cfd8919730ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -413,8 +413,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}/", name.unescaped().display(db))); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.as_str())); path }) } @@ -590,10 +589,10 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { Adt::Struct(s) => { - format!("struct.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("struct.{}.html", s.name(db).as_str()) } - Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())), - Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).as_str()), + Adt::Union(u) => format!("union.{}.html", u.name(db).as_str()), }, Definition::Crate(_) => String::from("index.html"), Definition::Module(m) => match m.name(db) { @@ -603,48 +602,48 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw) } - None => format!("{}/index.html", name.unescaped().display(db.upcast())), + None => format!("{}/index.html", name.as_str()), } } None => String::from("index.html"), }, Definition::Trait(t) => { - format!("trait.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("trait.{}.html", t.name(db).as_str()) } Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("traitalias.{}.html", t.name(db).as_str()) } Definition::TypeAlias(t) => { - format!("type.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("type.{}.html", t.name(db).as_str()) } Definition::BuiltinType(t) => { - format!("primitive.{}.html", t.name().unescaped().display(db.upcast())) + format!("primitive.{}.html", t.name().as_str()) } Definition::Function(f) => { - format!("fn.{}.html", f.name(db).unescaped().display(db.upcast())) + format!("fn.{}.html", f.name(db).as_str()) } Definition::Variant(ev) => { format!( "enum.{}.html#variant.{}", - ev.parent_enum(db).name(db).unescaped().display(db.upcast()), - ev.name(db).unescaped().display(db.upcast()) + ev.parent_enum(db).name(db).as_str(), + ev.name(db).as_str() ) } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast())) + format!("const.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { - format!("static.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("static.{}.html", s.name(db).as_str()) } Definition::Macro(mac) => match mac.kind(db) { hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { - format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("macro.{}.html", mac.name(db).as_str()) } hir::MacroKind::Derive => { - format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("derive.{}.html", mac.name(db).as_str()) } }, Definition::Field(field) => { @@ -654,11 +653,7 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some(( - def, - file, - Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))), - )); + return Some((def, file, Some(format!("structfield.{}", field.name(db).as_str())))); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -667,7 +662,7 @@ fn filename_and_frag_for_def( return Some((adt, file, Some(String::from("impl")))); } Definition::ExternCrateDecl(it) => { - format!("{}/index.html", it.name(db).unescaped().display(db.upcast())) + format!("{}/index.html", it.name(db).as_str()) } Definition::Local(_) | Definition::GenericParam(_) @@ -699,16 +694,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db).unescaped().display(db.upcast())) + format!("tymethod.{}", function.name(db).as_str()) } else { - format!("method.{}", function.name(db).unescaped().display(db.upcast())) + format!("method.{}", function.name(db).as_str()) } } AssocItem::Const(constant) => { - format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast())) + format!("associatedconstant.{}", constant.name(db)?.as_str()) } AssocItem::TypeAlias(ty) => { - format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast())) + format!("associatedtype.{}", ty.name(db).as_str()) } }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index a7b066700c54..a6f7e0c184ac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -124,9 +124,7 @@ fn should_hide_param_name_hint( } let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => { - Some(it.name(sema.db).unescaped().display_no_db().to_smolstr()) - } + hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()), _ => None, }; let fn_name = fn_name.as_deref(); diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index ba739df3092b..07dfd83c4eb7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -263,7 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#")) { Err(format_err!("Renaming aliases is currently unsupported")) } else { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 59c0c9dca1c4..0f80891404e5 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -459,6 +459,7 @@ define_symbols! { termination, test_case, test, + then, thiscall, trace_macros, transmute_opts, From 398cd2dbf63d67a0f58e1fa730883a7420f8ef70 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Tue, 21 Jan 2025 10:33:28 +0100 Subject: [PATCH 080/342] make large niche description more terse, switch to using u128::is_power_of_two --- src/tools/rust-analyzer/crates/ide/src/hover/render.rs | 8 ++++---- src/tools/rust-analyzer/crates/ide/src/hover/tests.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 7fe344cfa426..95be7c5618ef 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1090,7 +1090,7 @@ fn render_memory_layout( } else if is_pwr2minus1(niches) { format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); } else { - format_to!(label, "niches = really rather quite large, "); + format_to!(label, "niches = a lot, "); } } else { format_to!(label, "niches = {niches}, "); @@ -1224,15 +1224,15 @@ fn render_dyn_compatibility( } fn is_pwr2(val: u128) -> bool { - val.count_ones() == 1 + val.is_power_of_two() } fn is_pwr2minus1(val: u128) -> bool { - val == u128::MAX || (val + 1).count_ones() == 1 + val == u128::MAX || is_pwr2(val + 1) } fn is_pwr2plus1(val: u128) -> bool { - val != 0 && (val - 1).count_ones() == 1 + val != 0 && is_pwr2(val - 1) } fn pwr2_to_exponent(num: u128) -> String { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 63eb772fde23..064a845dc576 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1357,7 +1357,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = really rather quite large + size = 12 (0xC), align = 4, niches = a lot "#]], ); } From feb3fb5f19da41d350b7d5b94ce559dabd0fc5ee Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 20 Jan 2025 20:26:45 +0200 Subject: [PATCH 081/342] Sort completion items that skip `await` and `iter()` behind those that don't I don't think my ranking is perfect, because it places them even behind snippet completions, but this is something. --- .../crates/ide-completion/src/item.rs | 9 +++++++++ .../crates/ide-completion/src/render.rs | 16 ++++++++++++++++ .../crates/ide-completion/src/render/function.rs | 1 + 3 files changed, 26 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index dc2f9a768029..b0a096b64af2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -181,6 +181,8 @@ pub struct CompletionRelevance { pub postfix_match: Option, /// This is set for items that are function (associated or method) pub function: Option, + /// true when there is an `await.method()` or `iter().method()` completion. + pub is_skipping_completion: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -269,6 +271,7 @@ impl CompletionRelevance { postfix_match, trait_, function, + is_skipping_completion, } = self; // only applicable for completions within use items @@ -296,6 +299,12 @@ impl CompletionRelevance { score -= 5; } } + + // Lower rank for completions that skip `await` and `iter()`. + if is_skipping_completion { + score -= 7; + } + // lower rank for items that need an import if requires_import { score -= 1; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index ec26e311e88f..1b7adf1adb06 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -141,6 +141,7 @@ pub(crate) fn render_field( item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), exact_name_match: compute_exact_name_match(ctx.completion, &name), + is_skipping_completion: receiver.is_some(), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -213,6 +214,10 @@ pub(crate) fn render_tuple_field( ); item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string()) .lookup_by(field.to_string()); + item.set_relevance(CompletionRelevance { + is_skipping_completion: receiver.is_some(), + ..ctx.completion_relevance() + }); item.build(ctx.db()) } @@ -1333,6 +1338,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1362,6 +1368,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1451,6 +1458,7 @@ fn foo() { A { the$0 } } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -1509,6 +1517,7 @@ impl S { return_type: Other, }, ), + is_skipping_completion: false, }, }, CompletionItem { @@ -1651,6 +1660,7 @@ fn foo(s: S) { s.$0 } return_type: Other, }, ), + is_skipping_completion: false, }, }, ] @@ -1862,6 +1872,7 @@ fn f() -> i32 { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2622,6 +2633,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@107", }, @@ -2707,6 +2719,7 @@ fn foo() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2764,6 +2777,7 @@ fn main() { return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@92", }, @@ -3138,6 +3152,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, CompletionItem { @@ -3171,6 +3186,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 317c93b10f82..c3354902c3b7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -123,6 +123,7 @@ fn render( exact_name_match: compute_exact_name_match(completion, &call), function, trait_: trait_info, + is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))), ..ctx.completion_relevance() }); From f0f7204892c9faea1160a2a9ba32c7587c926c7e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 11:15:36 +0100 Subject: [PATCH 082/342] Fix Param::as_local treating closures wrong --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 41 ++++++++++------- .../crates/ide/src/inlay_hints.rs | 2 +- .../ide/src/inlay_hints/generic_param.rs | 30 ++++++++----- .../crates/ide/src/inlay_hints/param_name.rs | 45 +++++++++---------- 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4938478bb121..0cbc75726bf3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ body::BodyDiagnostic, data::{adt::VariantData, TraitFlags}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -2470,20 +2470,31 @@ impl Param { } pub fn as_local(&self, db: &dyn HirDatabase) -> Option { - let parent = match self.func { - Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it), - Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0, - _ => return None, - }; - let body = db.body(parent); - if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { - Some(Local { parent, binding_id: self_param }) - } else if let Pat::Bind { id, .. } = - &body[body.params[self.idx - body.self_param.is_some() as usize]] - { - Some(Local { parent, binding_id: *id }) - } else { - None + match self.func { + Callee::Def(CallableDefId::FunctionId(it)) => { + let parent = DefWithBodyId::FunctionId(it); + let body = db.body(parent); + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { + Some(Local { parent, binding_id: self_param }) + } else if let Pat::Bind { id, .. } = + &body[body.params[self.idx - body.self_param.is_some() as usize]] + { + Some(Local { parent, binding_id: *id }) + } else { + None + } + } + Callee::Closure(closure, _) => { + let c = db.lookup_intern_closure(closure.into()); + let body = db.body(c.0); + if let Expr::Closure { args, .. } = &body[c.1] { + if let Pat::Bind { id, .. } = &body[args[self.idx]] { + return Some(Local { parent: c.0, binding_id: *id }); + } + } + None + } + _ => None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6d83a747d766..94dd90c4aac0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -209,7 +209,7 @@ fn hints( ) { closing_brace::hints(hints, sema, config, file_id, node.clone()); if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); + generic_param::hints(hints, famous_defs, config, any_has_generic_args); } match_ast! { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 037b328d971f..13055757ba6a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -1,17 +1,19 @@ //! Implementation of inlay hints for generic parameters. -use ide_db::{active_parameter::generic_def_for_node, RootDatabase}; +use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs}; use syntax::{ ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, AstNode, }; -use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, +}; -use super::param_name::{is_argument_similar_to_param_name, render_label}; +use super::param_name::is_argument_similar_to_param_name; pub(crate) fn hints( acc: &mut Vec, - sema: &hir::Semantics<'_, RootDatabase>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, node: AnyHasGenericArgs, ) -> Option<()> { @@ -45,12 +47,11 @@ pub(crate) fn hints( return None; } - let name = param.name(sema.db); - let param_name = name.as_str(); + let param_name = param.name(sema.db); let should_hide = { let argument = get_string_representation(&arg)?; - is_argument_similar_to_param_name(&argument, param_name) + is_argument_similar_to_param_name(&argument, param_name.as_str()) }; if should_hide { @@ -64,7 +65,7 @@ pub(crate) fn hints( if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { return None; } - sema.source(it.merge())?.value.syntax().clone() + sema.source(it.merge()).map(|it| it.value.syntax().clone()) } hir::GenericParam::ConstParam(it) => { if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { @@ -72,17 +73,22 @@ pub(crate) fn hints( } let syntax = sema.source(it.merge())?.value.syntax().clone(); let const_param = ast::ConstParam::cast(syntax)?; - const_param.name()?.syntax().clone() + const_param.name().map(|it| it.syntax().clone()) } hir::GenericParam::LifetimeParam(it) => { if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { return None; } - sema.source(it)?.value.syntax().clone() + sema.source(it).map(|it| it.value.syntax().clone()) } }; - let linked_location = sema.original_range_opt(&source_syntax); - let label = render_label(param_name, config, linked_location); + let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + linked_location.map(Into::into), + ); Some(InlayHint { range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index a6f7e0c184ac..ac137d97d8ae 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -3,7 +3,6 @@ //! fn max(x: i32, y: i32) -> i32 { x + y } //! _ = max(/*x*/4, /*y*/4); //! ``` -use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; @@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - FamousDefs(sema, _): &FamousDefs<'_, '_>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, _file_id: EditionedFileId, expr: ast::Expr, @@ -37,23 +36,31 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; - let source = sema.source(p)?; - let (param_name, name_syntax) = match source.value.as_ref() { - Either::Left(pat) => (pat.name()?, pat.name()), - Either::Right(param) => match param.pat()? { - ast::Pat::IdentPat(it) => (it.name()?, it.name()), - _ => return None, - }, - }; - Some((name_syntax, param_name, arg, range)) + let param_name = p.name(sema.db)?; + Some((p, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) + !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg) }) .map(|(param, param_name, _, hir::FileRange { range, .. })| { - let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); + let linked_location = (|| { + let source = sema.source(param)?; + let name_syntax = match source.value.as_ref() { + Either::Left(pat) => pat.name(), + Either::Right(param) => match param.pat()? { + ast::Pat::IdentPat(it) => it.name(), + _ => None, + }, + }?; + sema.original_range_opt(name_syntax.syntax()) + })(); - let label = render_label(¶m_name, config, linked_location); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + linked_location.map(Into::into), + ); InlayHint { range, kind: InlayKind::Parameter, @@ -70,16 +77,6 @@ pub(super) fn hints( Some(()) } -pub(super) fn render_label( - param_name: impl Display, - config: &InlayHintsConfig, - linked_location: Option, -) -> InlayHintLabel { - let colon = if config.render_colons { ":" } else { "" }; - - InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into)) -} - fn get_callable( sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr, From fdcbd7111b227193d4355b8072ff075d1e1df6d0 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 21 Jan 2025 01:09:55 -0800 Subject: [PATCH 083/342] minor test cleanup - Removes some excess parens - Removes 3 duplicated tests --- .../pattern-errors.classic2024.stderr | 22 ++------ .../experimental/pattern-errors.rs | 5 +- .../pattern-errors.structural2024.stderr | 52 +++++++------------ .../experimental/well-typed-edition-2024.rs | 6 --- 4 files changed, 26 insertions(+), 59 deletions(-) diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr index 45ee489c5206..cc39e9b8ab74 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr @@ -71,19 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(x)) = &Some(Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:118:10 + --> $DIR/pattern-errors.rs:115:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -95,7 +83,7 @@ LL | let [&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:123:10 + --> $DIR/pattern-errors.rs:120:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -107,7 +95,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:10 + --> $DIR/pattern-errors.rs:125:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -119,7 +107,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:133:10 + --> $DIR/pattern-errors.rs:130:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ @@ -130,6 +118,6 @@ help: replace this `&mut` pattern with `&` LL | let [&&(mut x)] = &[&mut 0]; | ~ -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index 22ff0708d93a..d1f404a634b8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -29,15 +29,12 @@ pub fn main() { if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types } - if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { + if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { //[structural2024]~^ ERROR: mismatched types } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types } - if let Some(&mut Some(x)) = &Some(Some(0)) { - //~^ ERROR: mismatched types - } } fn structural_errors_0() { diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr index 3396d4148197..b996af4c1346 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr @@ -35,16 +35,16 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:32:29 + --> $DIR/pattern-errors.rs:32:28 | -LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^ +LL | if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { + | ^^^^^ | = note: cannot match inherited `&` with `&mut` pattern help: replace this `&mut` pattern with `&` | -LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) { - | ~ +LL | if let Some(&Some(Some(&_))) = &Some(Some(&mut Some(0))) { + | ~ error[E0308]: mismatched types --> $DIR/pattern-errors.rs:35:17 @@ -59,19 +59,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(x)) = &Some(Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:44:11 + --> $DIR/pattern-errors.rs:41:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^ @@ -83,7 +71,7 @@ LL | let &[&x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:49:11 + --> $DIR/pattern-errors.rs:46:11 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^ @@ -95,7 +83,7 @@ LL | let &[&x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:54:11 + --> $DIR/pattern-errors.rs:51:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^ @@ -107,7 +95,7 @@ LL | let &[&ref x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:59:11 + --> $DIR/pattern-errors.rs:56:11 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^ @@ -119,7 +107,7 @@ LL | let &[&ref x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:61:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^ @@ -131,7 +119,7 @@ LL | let &[&mut x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:69:11 + --> $DIR/pattern-errors.rs:66:11 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^ @@ -143,7 +131,7 @@ LL | let &[&mut x] = &mut &mut [0]; | ~ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:76:12 + --> $DIR/pattern-errors.rs:73:12 | LL | let [&(mut x)] = &[&0]; | ^^^^ @@ -153,7 +141,7 @@ LL | let [&(mut x)] = &[&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:80:12 + --> $DIR/pattern-errors.rs:77:12 | LL | let [&(mut x)] = &mut [&0]; | ^^^^ @@ -163,7 +151,7 @@ LL | let [&(mut x)] = &mut [&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:86:11 + --> $DIR/pattern-errors.rs:83:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^ @@ -175,7 +163,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:91:11 + --> $DIR/pattern-errors.rs:88:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^ @@ -187,7 +175,7 @@ LL | let [&&x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:96:11 + --> $DIR/pattern-errors.rs:93:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^ @@ -199,7 +187,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:101:11 + --> $DIR/pattern-errors.rs:98:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^ @@ -211,7 +199,7 @@ LL | let [&&ref x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:106:11 + --> $DIR/pattern-errors.rs:103:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^ @@ -223,7 +211,7 @@ LL | let [&&mut x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:111:11 + --> $DIR/pattern-errors.rs:108:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^ @@ -234,7 +222,7 @@ help: replace this `&mut` pattern with `&` LL | let [&&mut x] = &mut [&mut 0]; | ~ -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index 8a2593d0e3ab..906110d1dce8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -23,9 +23,6 @@ pub fn main() { if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { let _: u32 = x; } - if let Some(Some(&x)) = &Some(&Some(0)) { - let _: u32 = x; - } if let Some(&Some(&x)) = &mut Some(&Some(0)) { let _: u32 = x; } @@ -41,9 +38,6 @@ pub fn main() { if let Some(&Some(&x)) = &Some(&Some(0)) { let _: u32 = x; } - if let Some(&Some(&x)) = &Some(&mut Some(0)) { - let _: u32 = x; - } if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { let _: u32 = x; } From afd976b2b0382a91fba18f946a6272512895456a Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 21 Jan 2025 00:50:44 -0800 Subject: [PATCH 084/342] add more information to old tests --- .../pattern-errors.classic2024.stderr | 18 ++++----- .../experimental/pattern-errors.rs | 8 ++++ .../pattern-errors.structural2024.stderr | 38 +++++++++---------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr index cc39e9b8ab74..7b1abb6eaac3 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr @@ -11,7 +11,7 @@ LL | if let Some(&x) = &Some(&mut 0) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:14:17 + --> $DIR/pattern-errors.rs:15:17 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^^^^ @@ -23,7 +23,7 @@ LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:18:22 + --> $DIR/pattern-errors.rs:20:22 | LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { | ^^^^^ @@ -35,7 +35,7 @@ LL | if let Some(Some(&x)) = &Some(Some(&mut 0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:23:17 + --> $DIR/pattern-errors.rs:26:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -47,7 +47,7 @@ LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:29:23 + --> $DIR/pattern-errors.rs:34:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -59,7 +59,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:35:17 + --> $DIR/pattern-errors.rs:42:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -71,7 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:115:10 + --> $DIR/pattern-errors.rs:123:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -83,7 +83,7 @@ LL | let [&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:120:10 + --> $DIR/pattern-errors.rs:128:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:125:10 + --> $DIR/pattern-errors.rs:133:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:130:10 + --> $DIR/pattern-errors.rs:138:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index d1f404a634b8..14c32a053e31 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -9,31 +9,39 @@ pub fn main() { if let Some(&mut x) = &Some(&mut 0) { //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; } if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; } if let Some(&mut Some(&_)) = &Some(&Some(0)) { //~^ ERROR: mismatched types + //~| cannot match inherited `&` with `&mut` pattern } if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types + //~| cannot match inherited `&` with `&mut` pattern } if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { //[structural2024]~^ ERROR: mismatched types + //[structural2024]~| cannot match inherited `&` with `&mut` pattern } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types + //~| cannot match inherited `&` with `&mut` pattern } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr index b996af4c1346..05ff254ab980 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:23:17 + --> $DIR/pattern-errors.rs:26:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -11,7 +11,7 @@ LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:26:23 + --> $DIR/pattern-errors.rs:30:23 | LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { | ^^^^^ @@ -23,7 +23,7 @@ LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:29:23 + --> $DIR/pattern-errors.rs:34:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -35,7 +35,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:32:28 + --> $DIR/pattern-errors.rs:38:28 | LL | if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { | ^^^^^ @@ -47,7 +47,7 @@ LL | if let Some(&Some(Some(&_))) = &Some(Some(&mut Some(0))) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:35:17 + --> $DIR/pattern-errors.rs:42:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -59,7 +59,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:41:11 + --> $DIR/pattern-errors.rs:49:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^ @@ -71,7 +71,7 @@ LL | let &[&x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:46:11 + --> $DIR/pattern-errors.rs:54:11 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^ @@ -83,7 +83,7 @@ LL | let &[&x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:51:11 + --> $DIR/pattern-errors.rs:59:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let &[&ref x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:56:11 + --> $DIR/pattern-errors.rs:64:11 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let &[&ref x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:61:11 + --> $DIR/pattern-errors.rs:69:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^ @@ -119,7 +119,7 @@ LL | let &[&mut x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:66:11 + --> $DIR/pattern-errors.rs:74:11 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^ @@ -131,7 +131,7 @@ LL | let &[&mut x] = &mut &mut [0]; | ~ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:73:12 + --> $DIR/pattern-errors.rs:81:12 | LL | let [&(mut x)] = &[&0]; | ^^^^ @@ -141,7 +141,7 @@ LL | let [&(mut x)] = &[&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:77:12 + --> $DIR/pattern-errors.rs:85:12 | LL | let [&(mut x)] = &mut [&0]; | ^^^^ @@ -151,7 +151,7 @@ LL | let [&(mut x)] = &mut [&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:83:11 + --> $DIR/pattern-errors.rs:91:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^ @@ -163,7 +163,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:88:11 + --> $DIR/pattern-errors.rs:96:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^ @@ -175,7 +175,7 @@ LL | let [&&x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:93:11 + --> $DIR/pattern-errors.rs:101:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^ @@ -187,7 +187,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:98:11 + --> $DIR/pattern-errors.rs:106:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^ @@ -199,7 +199,7 @@ LL | let [&&ref x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:103:11 + --> $DIR/pattern-errors.rs:111:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^ @@ -211,7 +211,7 @@ LL | let [&&mut x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:108:11 + --> $DIR/pattern-errors.rs:116:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^ From 4ed44c9bd6608bb945bedf69b91dbaa6bdf0c6b7 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 21 Jan 2025 02:07:32 -0800 Subject: [PATCH 085/342] add a stable edition 2021 revision to pattern typing tests This serves two purposes. First, they're additional tests that stable Rust behavior hasn't been messed with. There's plenty of other pattern tests, so this is less important, but these at least are targeted at what's being changed. Second, this helps document exactly where the new rulesets agree and disagree with stable pattern typing. This will be especially important after the new rules for old editions are updated, since they need to be strictly more permissive; any patterns well-typed on stable should also be well-typed with the same resultant bindings on the (upcoming) new new old-edition rules. The unusual test ordering on `borrowck-errors.rs` and `ref-binding-on-inh-ref-errors.rs` are to hopefully reduce how much adding new tests will mess with line numbers in their stderr. --- .../borrowck-errors.classic2024.stderr | 70 ++--- .../experimental/borrowck-errors.rs | 37 ++- .../borrowck-errors.stable2021.stderr | 69 +++++ .../borrowck-errors.structural2024.stderr | 4 +- .../mut-ref-mut.classic2024.stderr | 4 +- .../experimental/mut-ref-mut.rs | 19 +- .../mut-ref-mut.structural2024.stderr | 4 +- .../pattern-errors.classic2024.stderr | 20 +- .../experimental/pattern-errors.rs | 84 ++++-- .../pattern-errors.stable2021.stderr | 283 ++++++++++++++++++ .../pattern-errors.structural2024.stderr | 38 +-- ...nding-on-inh-ref-errors.classic2024.stderr | 70 +++++ .../ref-binding-on-inh-ref-errors.rs | 53 +++- ...inding-on-inh-ref-errors.stable2021.stderr | 35 +++ ...ng-on-inh-ref-errors.structural2024.stderr | 56 ++-- ...ut-inside-shared-ref-pat.classic2024.fixed | 26 +- ...t-inside-shared-ref-pat.classic2024.stderr | 12 +- .../ref-mut-inside-shared-ref-pat.rs | 26 +- ...ut-inside-shared-ref-pat.stable2021.stderr | 58 ++++ ...inside-shared-ref-pat.structural2024.fixed | 26 +- ...nside-shared-ref-pat.structural2024.stderr | 10 +- .../experimental/well-typed-edition-2024.rs | 49 ++- .../well-typed-edition-2024.stable2021.stderr | 260 ++++++++++++++++ 23 files changed, 1109 insertions(+), 204 deletions(-) create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr create mode 100644 tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr index d72539eeeb2f..331b86736a0b 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr @@ -1,5 +1,33 @@ +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:13:16 + | +LL | let [&x] = &[&mut 0]; + | - ^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&ref x] = &[&mut 0]; + | +++ + +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:19:16 + | +LL | let [&x] = &mut [&mut 0]; + | - ^^^^^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&ref x] = &mut [&mut 0]; + | +++ + error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:9:29 + --> $DIR/borrowck-errors.rs:27:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -14,59 +42,31 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:14:10 + --> $DIR/borrowck-errors.rs:32:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:17:23 + --> $DIR/borrowck-errors.rs:35:23 | LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { | ^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:22:11 + --> $DIR/borrowck-errors.rs:40:11 | LL | let &[x] = &&mut [0]; | ^ cannot borrow as mutable -error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:26:16 - | -LL | let [&x] = &[&mut 0]; - | - ^^^^^^^^^ cannot move out of here - | | - | data moved here - | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait - | -help: consider borrowing the pattern binding - | -LL | let [&ref x] = &[&mut 0]; - | +++ - -error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:30:16 - | -LL | let [&x] = &mut [&mut 0]; - | - ^^^^^^^^^^^^^ cannot move out of here - | | - | data moved here - | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait - | -help: consider borrowing the pattern binding - | -LL | let [&ref x] = &mut [&mut 0]; - | +++ - -error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array - --> $DIR/borrowck-errors.rs:34:20 +error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:44:20 | LL | let [&mut x] = &mut [&mut 0]; | - ^^^^^^^^^^^^^ cannot move out of here | | | data moved here - | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait | help: consider borrowing the pattern binding | diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs index 9107d3ebf6ac..3f5d9c8db5d8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -1,10 +1,28 @@ -//@ edition: 2024 -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 //! Tests for pattern errors not handled by the pattern typing rules, but by borrowck. #![allow(incomplete_features)] #![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] #![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] +/// These patterns additionally use `&` to match a `&mut` reference type, which causes compilation +/// to fail in HIR typeck on stable. As such, they need to be separate from the other tests. +fn errors_caught_in_hir_typeck_on_stable() { + let [&x] = &[&mut 0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + //[classic2024]~^^^ ERROR: cannot move out of type + let _: &u32 = x; + + let [&x] = &mut [&mut 0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + //[classic2024]~^^^ ERROR: cannot move out of type + let _: &u32 = x; +} + pub fn main() { if let Some(&Some(x)) = Some(&Some(&mut 0)) { //~^ ERROR: cannot move out of a shared reference [E0507] @@ -15,23 +33,16 @@ pub fn main() { //~^ cannot borrow data in a `&` reference as mutable [E0596] if let &Some(Some(x)) = &Some(&mut Some(0)) { - //[classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable + //[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable let _: &u32 = x; } let &[x] = &&mut [0]; - //[classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable - let _: &u32 = x; - - let [&x] = &[&mut 0]; - //[classic2024]~^ ERROR: cannot move out of type - let _: &u32 = x; - - let [&x] = &mut [&mut 0]; - //[classic2024]~^ ERROR: cannot move out of type + //[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable let _: &u32 = x; let [&mut x] = &mut [&mut 0]; //[classic2024]~^ ERROR: cannot move out of type - let _: &mut u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &mut u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr new file mode 100644 index 000000000000..65c98e2da9c3 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr @@ -0,0 +1,69 @@ +error[E0308]: mismatched types + --> $DIR/borrowck-errors.rs:13:10 + | +LL | let [&x] = &[&mut 0]; + | ^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&x] = &[&mut 0]; +LL + let [x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/borrowck-errors.rs:19:10 + | +LL | let [&x] = &mut [&mut 0]; + | ^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&x] = &mut [&mut 0]; +LL + let [x] = &mut [&mut 0]; + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/borrowck-errors.rs:27:29 + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | - ^^^^^^^^^^^^^^^^^^^ + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) { +LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { + | + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:32:10 + | +LL | let &ref mut x = &0; + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:35:23 + | +LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { + | ^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:40:11 + | +LL | let &[x] = &&mut [0]; + | ^ cannot borrow as mutable + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0507, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr index c62461140750..30d2f9f3d702 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of a shared reference - --> $DIR/borrowck-errors.rs:9:29 + --> $DIR/borrowck-errors.rs:27:29 | LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | - ^^^^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { | error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/borrowck-errors.rs:14:10 + --> $DIR/borrowck-errors.rs:32:10 | LL | let &ref mut x = &0; | ^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr index 43560a180302..3849b4ed9894 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr @@ -1,5 +1,5 @@ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:11:13 + --> $DIR/mut-ref-mut.rs:14:13 | LL | let Foo(mut a) = &Foo(0); | ^^^^ @@ -9,7 +9,7 @@ LL | let Foo(mut a) = &Foo(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:15:13 + --> $DIR/mut-ref-mut.rs:19:13 | LL | let Foo(mut a) = &mut Foo(0); | ^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs index c2fb3f287092..68d6871eabfa 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs @@ -1,7 +1,10 @@ -//@ edition: 2024 -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[stable2021] run-pass //! Test diagnostics for binding with `mut` when the default binding mode is by-ref. -#![allow(incomplete_features)] +#![allow(incomplete_features, unused_assignments, unused_variables)] #![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] #![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] @@ -9,10 +12,12 @@ pub fn main() { struct Foo(u8); let Foo(mut a) = &Foo(0); - //~^ ERROR: binding cannot be both mutable and by-reference - a = &42; + //[classic2024,structural2024]~^ ERROR: binding cannot be both mutable and by-reference + #[cfg(stable2021)] { a = 42 } + #[cfg(any(classic2024, structural2024))] { a = &42 } let Foo(mut a) = &mut Foo(0); - //~^ ERROR: binding cannot be both mutable and by-reference - a = &mut 42; + //[classic2024,structural2024]~^ ERROR: binding cannot be both mutable and by-reference + #[cfg(stable2021)] { a = 42 } + #[cfg(any(classic2024, structural2024))] { a = &mut 42 } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr index 43560a180302..3849b4ed9894 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr @@ -1,5 +1,5 @@ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:11:13 + --> $DIR/mut-ref-mut.rs:14:13 | LL | let Foo(mut a) = &Foo(0); | ^^^^ @@ -9,7 +9,7 @@ LL | let Foo(mut a) = &Foo(0); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/mut-ref-mut.rs:15:13 + --> $DIR/mut-ref-mut.rs:19:13 | LL | let Foo(mut a) = &mut Foo(0); | ^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr index 7b1abb6eaac3..3a124dcead52 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:10:17 + --> $DIR/pattern-errors.rs:12:17 | LL | if let Some(&mut x) = &Some(&mut 0) { | ^^^^^ @@ -11,7 +11,7 @@ LL | if let Some(&x) = &Some(&mut 0) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:15:17 + --> $DIR/pattern-errors.rs:18:17 | LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { | ^^^^^ @@ -23,7 +23,7 @@ LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:20:22 + --> $DIR/pattern-errors.rs:24:22 | LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { | ^^^^^ @@ -35,7 +35,7 @@ LL | if let Some(Some(&x)) = &Some(Some(&mut 0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:26:17 + --> $DIR/pattern-errors.rs:31:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -47,7 +47,7 @@ LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:34:23 + --> $DIR/pattern-errors.rs:41:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -59,7 +59,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:42:17 + --> $DIR/pattern-errors.rs:51:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -71,7 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:123:10 + --> $DIR/pattern-errors.rs:147:10 | LL | let [&mut x] = &[&mut 0]; | ^^^^^ @@ -83,7 +83,7 @@ LL | let [&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:128:10 + --> $DIR/pattern-errors.rs:153:10 | LL | let [&mut &x] = &[&mut 0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:133:10 + --> $DIR/pattern-errors.rs:159:10 | LL | let [&mut &ref x] = &[&mut 0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:138:10 + --> $DIR/pattern-errors.rs:165:10 | LL | let [&mut &(mut x)] = &[&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs index 14c32a053e31..c07c2972cd05 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -1,5 +1,7 @@ -//@ edition: 2024 -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 //! Test cases for poorly-typed patterns in edition 2024 which are caught by HIR typeck. These must //! be separate from cases caught by MIR borrowck or the latter errors may not be emitted. #![allow(incomplete_features)] @@ -10,69 +12,83 @@ pub fn main() { if let Some(&mut x) = &Some(&mut 0) { //[classic2024]~^ ERROR: mismatched types //[classic2024]~| cannot match inherited `&` with `&mut` pattern - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &u32 = x; } if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { - //[classic2024]~^ ERROR: mismatched types + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { //[classic2024]~^ ERROR: mismatched types //[classic2024]~| cannot match inherited `&` with `&mut` pattern - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &u32 = x; } if let Some(&mut Some(&_)) = &Some(&Some(0)) { //~^ ERROR: mismatched types - //~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| types differ in mutability + //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern } if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern } if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { //~^ ERROR: mismatched types - //~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| expected integer, found `&mut _` + //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern } if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` //[structural2024]~| cannot match inherited `&` with `&mut` pattern } if let Some(&mut Some(x)) = &Some(Some(0)) { //~^ ERROR: mismatched types - //~| cannot match inherited `&` with `&mut` pattern + //[stable2021]~| expected `Option<{integer}>`, found `&mut _` + //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern } } fn structural_errors_0() { let &[&mut x] = &&mut [0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let &[&mut x] = &mut &mut [0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let &[&mut ref x] = &&mut [0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let &[&mut ref x] = &mut &mut [0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let &[&mut mut x] = &&mut [0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let &[&mut mut x] = &mut &mut [0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } @@ -80,41 +96,49 @@ fn structural_errors_0() { fn structural_errors_1() { let [&(mut x)] = &[&0]; //[structural2024]~^ ERROR: binding cannot be both mutable and by-reference - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(classic2024)] let _: &u32 = x; let [&(mut x)] = &mut [&0]; //[structural2024]~^ ERROR: binding cannot be both mutable and by-reference - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(classic2024)] let _: &u32 = x; } fn structural_errors_2() { let [&&mut x] = &[&mut 0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&&mut x] = &mut [&mut 0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&&mut ref x] = &[&mut 0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&&mut ref x] = &mut [&mut 0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&&mut mut x] = &[&mut 0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&&mut mut x] = &mut [&mut 0]; - //[structural2024]~^ ERROR: mismatched types + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability //[structural2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } @@ -123,20 +147,24 @@ fn classic_errors_0() { let [&mut x] = &[&mut 0]; //[classic2024]~^ ERROR: mismatched types //[classic2024]~| cannot match inherited `&` with `&mut` pattern - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &u32 = x; let [&mut &x] = &[&mut 0]; - //[classic2024]~^ ERROR: mismatched types + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; let [&mut &ref x] = &[&mut 0]; - //[classic2024]~^ ERROR: mismatched types + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: &u32 = x; let [&mut &(mut x)] = &[&mut 0]; - //[classic2024]~^ ERROR: mismatched types + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` //[classic2024]~| cannot match inherited `&` with `&mut` pattern let _: u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr new file mode 100644 index 000000000000..e7eb1813846e --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr @@ -0,0 +1,283 @@ +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:18:27 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:31:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected reference `&Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:36:17 + | +LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { + | ^^^^^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:41:23 + | +LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:46:17 + | +LL | if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { + | ^^^^^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected `Option<&mut Option<{integer}>>`, found `&_` + | + = note: expected enum `Option<&mut Option<{integer}>>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:51:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&mut _` + | + = note: expected enum `Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:59:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:59:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut x] = &&mut [0]; +LL + let &[x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:65:9 + | +LL | let &[&mut x] = &mut &mut [0]; + | ^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:71:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:71:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut ref x] = &&mut [0]; +LL + let &[ref x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:77:9 + | +LL | let &[&mut ref x] = &mut &mut [0]; + | ^^^^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:83:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:83:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut mut x] = &&mut [0]; +LL + let &[mut x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:89:9 + | +LL | let &[&mut mut x] = &mut &mut [0]; + | ^^^^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:109:10 + | +LL | let [&&mut x] = &[&mut 0]; + | ^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:115:10 + | +LL | let [&&mut x] = &mut [&mut 0]; + | ^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:121:10 + | +LL | let [&&mut ref x] = &[&mut 0]; + | ^^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:127:10 + | +LL | let [&&mut ref x] = &mut [&mut 0]; + | ^^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:133:10 + | +LL | let [&&mut mut x] = &[&mut 0]; + | ^^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:139:10 + | +LL | let [&&mut mut x] = &mut [&mut 0]; + | ^^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:153:15 + | +LL | let [&mut &x] = &[&mut 0]; + | ^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&mut &x] = &[&mut 0]; +LL + let [&mut x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:159:15 + | +LL | let [&mut &ref x] = &[&mut 0]; + | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&mut &ref x] = &[&mut 0]; +LL + let [&mut ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:165:15 + | +LL | let [&mut &(mut x)] = &[&mut 0]; + | ^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&mut &(mut x)] = &[&mut 0]; +LL + let [&mut mut x)] = &[&mut 0]; + | + +error: aborting due to 21 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr index 05ff254ab980..861ed2216cd9 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:26:17 + --> $DIR/pattern-errors.rs:31:17 | LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { | ^^^^^ @@ -11,7 +11,7 @@ LL | if let Some(&Some(&_)) = &Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:30:23 + --> $DIR/pattern-errors.rs:36:23 | LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { | ^^^^^ @@ -23,7 +23,7 @@ LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:34:23 + --> $DIR/pattern-errors.rs:41:23 | LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { | ^^^^^ @@ -35,7 +35,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:38:28 + --> $DIR/pattern-errors.rs:46:28 | LL | if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { | ^^^^^ @@ -47,7 +47,7 @@ LL | if let Some(&Some(Some(&_))) = &Some(Some(&mut Some(0))) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:42:17 + --> $DIR/pattern-errors.rs:51:17 | LL | if let Some(&mut Some(x)) = &Some(Some(0)) { | ^^^^^ @@ -59,7 +59,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:49:11 + --> $DIR/pattern-errors.rs:59:11 | LL | let &[&mut x] = &&mut [0]; | ^^^^^ @@ -71,7 +71,7 @@ LL | let &[&x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:54:11 + --> $DIR/pattern-errors.rs:65:11 | LL | let &[&mut x] = &mut &mut [0]; | ^^^^^ @@ -83,7 +83,7 @@ LL | let &[&x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:59:11 + --> $DIR/pattern-errors.rs:71:11 | LL | let &[&mut ref x] = &&mut [0]; | ^^^^^ @@ -95,7 +95,7 @@ LL | let &[&ref x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:64:11 + --> $DIR/pattern-errors.rs:77:11 | LL | let &[&mut ref x] = &mut &mut [0]; | ^^^^^ @@ -107,7 +107,7 @@ LL | let &[&ref x] = &mut &mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:69:11 + --> $DIR/pattern-errors.rs:83:11 | LL | let &[&mut mut x] = &&mut [0]; | ^^^^^ @@ -119,7 +119,7 @@ LL | let &[&mut x] = &&mut [0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:74:11 + --> $DIR/pattern-errors.rs:89:11 | LL | let &[&mut mut x] = &mut &mut [0]; | ^^^^^ @@ -131,7 +131,7 @@ LL | let &[&mut x] = &mut &mut [0]; | ~ error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:81:12 + --> $DIR/pattern-errors.rs:97:12 | LL | let [&(mut x)] = &[&0]; | ^^^^ @@ -141,7 +141,7 @@ LL | let [&(mut x)] = &[&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/pattern-errors.rs:85:12 + --> $DIR/pattern-errors.rs:102:12 | LL | let [&(mut x)] = &mut [&0]; | ^^^^ @@ -151,7 +151,7 @@ LL | let [&(mut x)] = &mut [&0]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:91:11 + --> $DIR/pattern-errors.rs:109:11 | LL | let [&&mut x] = &[&mut 0]; | ^^^^^ @@ -163,7 +163,7 @@ LL | let [&&x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:96:11 + --> $DIR/pattern-errors.rs:115:11 | LL | let [&&mut x] = &mut [&mut 0]; | ^^^^^ @@ -175,7 +175,7 @@ LL | let [&&x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:101:11 + --> $DIR/pattern-errors.rs:121:11 | LL | let [&&mut ref x] = &[&mut 0]; | ^^^^^ @@ -187,7 +187,7 @@ LL | let [&&ref x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:106:11 + --> $DIR/pattern-errors.rs:127:11 | LL | let [&&mut ref x] = &mut [&mut 0]; | ^^^^^ @@ -199,7 +199,7 @@ LL | let [&&ref x] = &mut [&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:111:11 + --> $DIR/pattern-errors.rs:133:11 | LL | let [&&mut mut x] = &[&mut 0]; | ^^^^^ @@ -211,7 +211,7 @@ LL | let [&&mut x] = &[&mut 0]; | ~ error[E0308]: mismatched types - --> $DIR/pattern-errors.rs:116:11 + --> $DIR/pattern-errors.rs:139:11 | LL | let [&&mut mut x] = &mut [&mut 0]; | ^^^^^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr new file mode 100644 index 000000000000..70cdcbd62eb0 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr @@ -0,0 +1,70 @@ +error[E0308]: mismatched types + --> $DIR/ref-binding-on-inh-ref-errors.rs:60:10 + | +LL | let [&mut ref x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | let [&ref x] = &[&mut 0]; + | ~ + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[ref mut x] = &[0]; + | + + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^ cannot borrow as mutable + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10 + | +LL | let [ref x] = &[0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[ref x] = &[0]; + | + + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:88:10 + | +LL | let [ref x] = &mut [0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [ref x] = &mut [0]; + | ++++ + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:93:10 + | +LL | let [ref mut x] = &mut [0]; + | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [ref mut x] = &mut [0]; + | ++++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs index de06489b9f81..a151c84739c1 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -1,40 +1,63 @@ -//@ edition: 2024 -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 //@[classic2024] run-pass -//! Tests for errors from binding with `ref x` under a by-ref default binding mode. These can't be -//! in the same body as tests for other errors, since they're emitted during THIR construction. +//! Tests for errors from binding with `ref x` under a by-ref default binding mode in edition 2024. +//! These can't be in the same body as tests for other errors, since they're emitted during THIR +//! construction. The errors on stable edition 2021 Rust are unrelated. #![allow(incomplete_features)] #![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] #![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] -pub fn main() { +/// These only fail on the eat-inner variant of the new edition 2024 pattern typing rules. +/// The eat-outer variant eats the inherited reference, so binding with `ref` isn't a problem. +fn errors_from_eating_the_real_reference() { let [&ref x] = &[&0]; //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &u32 = x; #[cfg(classic2024)] let _: &&u32 = x; - let [&ref x] = &[&mut 0]; - //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural2024]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic2024)] let _: &&mut u32 = x; - let [&ref x] = &mut [&0]; //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &u32 = x; #[cfg(classic2024)] let _: &&u32 = x; - let [&ref x] = &mut [&mut 0]; - //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 - //[structural2024]~| cannot override to bind by-reference when that is the implicit default - #[cfg(classic2024)] let _: &&mut u32 = x; - let [&mut ref x] = &mut [&mut 0]; //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &u32 = x; #[cfg(classic2024)] let _: &&mut u32 = x; let [&mut ref mut x] = &mut [&mut 0]; //[structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &mut u32 = x; #[cfg(classic2024)] let _: &mut &mut u32 = x; + + errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable(); +} + +/// To make absolutely sure binding with `ref` ignores inherited references on stable, let's +/// quarantine these typeck errors (from using a `&` pattern to match a `&mut` reference type). +fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() { + let [&ref x] = &[&mut 0]; + //[stable2021]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~^^^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&mut u32 = x; + + let [&ref x] = &mut [&mut 0]; + //[stable2021]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~^^^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(classic2024)] let _: &&mut u32 = x; +} + +pub fn main() { + errors_from_eating_the_real_reference(); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr new file mode 100644 index 000000000000..c0518130368d --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr @@ -0,0 +1,35 @@ +error[E0308]: mismatched types + --> $DIR/ref-binding-on-inh-ref-errors.rs:46:10 + | +LL | let [&ref x] = &[&mut 0]; + | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&ref x] = &[&mut 0]; +LL + let [ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/ref-binding-on-inh-ref-errors.rs:53:10 + | +LL | let [&ref x] = &mut [&mut 0]; + | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&ref x] = &mut [&mut 0]; +LL + let [ref x] = &mut [&mut 0]; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr index a952f72f08e9..92aa8f1ea685 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr @@ -1,5 +1,5 @@ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:11:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:16:11 | LL | let [&ref x] = &[&0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -11,19 +11,7 @@ LL | let &[&ref x] = &[&0]; | + error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:16:11 - | -LL | let [&ref x] = &[&mut 0]; - | ^^^ cannot override to bind by-reference when that is the implicit default - | - = note: for more information, see -help: make the implied reference pattern explicit - | -LL | let &[&ref x] = &[&mut 0]; - | + - -error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:21:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:22:11 | LL | let [&ref x] = &mut [&0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -35,19 +23,7 @@ LL | let &mut [&ref x] = &mut [&0]; | ++++ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:26:11 - | -LL | let [&ref x] = &mut [&mut 0]; - | ^^^ cannot override to bind by-reference when that is the implicit default - | - = note: for more information, see -help: make the implied reference pattern explicit - | -LL | let &mut [&ref x] = &mut [&mut 0]; - | ++++ - -error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:31:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:28:15 | LL | let [&mut ref x] = &mut [&mut 0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -59,7 +35,7 @@ LL | let &mut [&mut ref x] = &mut [&mut 0]; | ++++ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:36:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:34:15 | LL | let [&mut ref mut x] = &mut [&mut 0]; | ^^^^^^^ cannot override to bind by-reference when that is the implicit default @@ -70,5 +46,29 @@ help: make the implied reference pattern explicit LL | let &mut [&mut ref mut x] = &mut [&mut 0]; | ++++ +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:46:11 + | +LL | let [&ref x] = &[&mut 0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[&ref x] = &[&mut 0]; + | + + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:53:11 + | +LL | let [&ref x] = &mut [&mut 0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [&ref x] = &mut [&mut 0]; + | ++++ + error: aborting due to 6 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed index 82c40878f493..c01784d5076b 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed @@ -1,6 +1,9 @@ -//@ edition: 2024 -//@ run-rustfix -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-rustfix +//@[structural2024] run-rustfix //! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts //! to bind by mutable reference. #![allow(incomplete_features)] @@ -9,12 +12,14 @@ pub fn main() { if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; } if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; } @@ -22,16 +27,19 @@ pub fn main() { ($var:ident) => { ref mut $var }; } let &mut pat!(x) = &mut 0; - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; let &mut (ref mut a, ref mut b) = &mut (true, false); - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - //~| ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024,structural2024]~| ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut bool = a; let _: &mut bool = b; let &mut [x] = &mut &mut [0]; - //[classic2024]~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr index 42a4a8597f7a..5e98b77be40c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr @@ -1,5 +1,5 @@ error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:11:31 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:14:31 | LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { | - ^ @@ -7,7 +7,7 @@ LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:16:31 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:20:31 | LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { | - ^ @@ -15,7 +15,7 @@ LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:24:15 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:29:15 | LL | let &pat!(x) = &mut 0; | - ^ @@ -23,7 +23,7 @@ LL | let &pat!(x) = &mut 0; | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:28:19 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:19 | LL | let &(ref mut a, ref mut b) = &mut (true, false); | - ^ @@ -31,7 +31,7 @@ LL | let &(ref mut a, ref mut b) = &mut (true, false); | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:28:30 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:30 | LL | let &(ref mut a, ref mut b) = &mut (true, false); | - ^ @@ -39,7 +39,7 @@ LL | let &(ref mut a, ref mut b) = &mut (true, false); | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:11 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:41:11 | LL | let &[x] = &mut &mut [0]; | - ^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs index 62b9037fac4c..fe40dabb5539 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs @@ -1,6 +1,9 @@ -//@ edition: 2024 -//@ run-rustfix -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-rustfix +//@[structural2024] run-rustfix //! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts //! to bind by mutable reference. #![allow(incomplete_features)] @@ -9,12 +12,14 @@ pub fn main() { if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; } if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; } @@ -22,16 +27,19 @@ pub fn main() { ($var:ident) => { ref mut $var }; } let &pat!(x) = &mut 0; - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; let &(ref mut a, ref mut b) = &mut (true, false); - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - //~| ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024,structural2024]~| ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut bool = a; let _: &mut bool = b; let &[x] = &mut &mut [0]; - //[classic2024]~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr new file mode 100644 index 000000000000..72c6c05e1844 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr @@ -0,0 +1,58 @@ +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:14:17 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | ^^^^^^^^^^^^^^^^ ------------------ this expression has type `&mut Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:20:12 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | ^^^^^^^^^^^^^^^^^^^^^^ ------------------ this expression has type `&mut Option>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:29:9 + | +LL | let &pat!(x) = &mut 0; + | ^^^^^^^^ ------ this expression has type `&mut {integer}` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:9 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | ^^^^^^^^^^^^^^^^^^^^^^^ ------------------ this expression has type `&mut (bool, bool)` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut (bool, bool)` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:41:9 + | +LL | let &[x] = &mut &mut [0]; + | ^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed index 32e955db12ce..4ee849b38c53 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed @@ -1,6 +1,9 @@ -//@ edition: 2024 -//@ run-rustfix -//@ revisions: classic2024 structural2024 +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-rustfix +//@[structural2024] run-rustfix //! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts //! to bind by mutable reference. #![allow(incomplete_features)] @@ -9,12 +12,14 @@ pub fn main() { if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; } if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; } @@ -22,16 +27,19 @@ pub fn main() { ($var:ident) => { ref mut $var }; } let &mut pat!(x) = &mut 0; - //~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut u8 = x; let &mut (ref mut a, ref mut b) = &mut (true, false); - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - //~| ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024,structural2024]~| ERROR: cannot borrow as mutable inside an `&` pattern let _: &mut bool = a; let _: &mut bool = b; let &[x] = &mut &mut [0]; - //[classic2024]~^ ERROR: cannot borrow as mutable inside an `&` pattern + //[stable2021]~^ ERROR: mismatched types + //[classic2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern let _: &u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr index 6c384a51fac1..69cb6c438b6e 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr @@ -1,5 +1,5 @@ error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:11:31 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:14:31 | LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { | - ^ @@ -7,7 +7,7 @@ LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:16:31 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:20:31 | LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { | - ^ @@ -15,7 +15,7 @@ LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:24:15 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:29:15 | LL | let &pat!(x) = &mut 0; | - ^ @@ -23,7 +23,7 @@ LL | let &pat!(x) = &mut 0; | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:28:19 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:19 | LL | let &(ref mut a, ref mut b) = &mut (true, false); | - ^ @@ -31,7 +31,7 @@ LL | let &(ref mut a, ref mut b) = &mut (true, false); | help: replace this `&` with `&mut`: `&mut` error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref-mut-inside-shared-ref-pat.rs:28:30 + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:30 | LL | let &(ref mut a, ref mut b) = &mut (true, false); | - ^ diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index 906110d1dce8..a8ad014fc5d3 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -1,6 +1,9 @@ -//@ edition: 2024 -//@ revisions: classic2024 structural2024 -//@ run-pass +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-pass +//@[structural2024] run-pass //! Test cases for well-typed patterns in edition 2024. These are in their own file to ensure we //! pass both HIR typeck and MIR borrowck, as we may skip the latter if grouped with failing tests. #![allow(incomplete_features, unused_mut)] @@ -9,65 +12,101 @@ pub fn main() { if let Some(Some(&x)) = &Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` let _: u32 = x; } if let Some(Some(&x)) = &Some(Some(&0)) { - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(any(classic2024, structural2024))] let _: &u32 = x; } if let Some(Some(&&x)) = &Some(Some(&0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` let _: u32 = x; } if let Some(&Some(x)) = &Some(Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<{integer}>`, found `&_` let _: u32 = x; } if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&mut _` let _: u32 = x; } if let Some(&Some(&x)) = &mut Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` let _: u32 = x; } if let Some(&Some(x)) = &mut Some(&Some(0)) { - let _: &u32 = x; + #[cfg(stable2021)] let _: u32 = x; + #[cfg(any(classic2024, structural2024))] let _: &u32 = x; } if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { let _: &u32 = x; } if let Some(&Some(&x)) = &Some(&mut Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: u32 = x; } if let Some(&Some(&x)) = &Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` let _: u32 = x; } if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` let _: u32 = x; } if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: u32 = x; } if let Some(&Some(x)) = &mut Some(Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<{integer}>`, found `&_` let _: u32 = x; } // Tests for eat-inner rulesets matching on the outer reference if matching on the inner // reference causes a mutability mismatch, i.e. `Deref(EatInner, FallbackToOuter)`: let [&mut x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: &u32 = x; let [&mut ref x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: &&u32 = x; let [&mut ref mut x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: &mut &u32 = x; let [&mut mut x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: &u32 = x; let [&mut &x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: u32 = x; let [&mut &ref x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: &u32 = x; let [&mut &(mut x)] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability let _: u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr new file mode 100644 index 000000000000..c78459bb21cf --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr @@ -0,0 +1,260 @@ +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:14:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:23:23 + | +LL | if let Some(Some(&&x)) = &Some(Some(&0)) { + | ^^ --------------- this expression has type `&Option>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&&x)) = &Some(Some(&0)) { +LL + if let Some(Some(&x)) = &Some(Some(&0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:28:17 + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:33:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:33:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:38:23 + | +LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { + | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:50:17 + | +LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { + | ^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:55:23 + | +LL | if let Some(&Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:60:17 + | +LL | if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + | ^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected `Option<&mut Option<{integer}>>`, found `&_` + | + = note: expected enum `Option<&mut Option<{integer}>>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:65:23 + | +LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:70:17 + | +LL | if let Some(&Some(x)) = &mut Some(Some(0)) { + | ^^^^^^^^ ------------------ this expression has type `&mut Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:78:10 + | +LL | let [&mut x] = &mut [&0]; + | ^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:78:10 + | +LL | let [&mut x] = &mut [&0]; + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut x] = &mut [&0]; +LL + let [x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:83:10 + | +LL | let [&mut ref x] = &mut [&0]; + | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:83:10 + | +LL | let [&mut ref x] = &mut [&0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut ref x] = &mut [&0]; +LL + let [ref x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:88:10 + | +LL | let [&mut ref mut x] = &mut [&0]; + | ^^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:88:10 + | +LL | let [&mut ref mut x] = &mut [&0]; + | ^^^^^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut ref mut x] = &mut [&0]; +LL + let [ref mut x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:93:10 + | +LL | let [&mut mut x] = &mut [&0]; + | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:93:10 + | +LL | let [&mut mut x] = &mut [&0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut mut x] = &mut [&0]; +LL + let [mut x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:98:10 + | +LL | let [&mut &x] = &mut [&0]; + | ^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:103:10 + | +LL | let [&mut &ref x] = &mut [&0]; + | ^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:108:10 + | +LL | let [&mut &(mut x)] = &mut [&0]; + | ^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` + +error: aborting due to 17 previous errors + +For more information about this error, try `rustc --explain E0308`. From e08f6d45305617b180856ae75969aac6c0db3a80 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Tue, 21 Jan 2025 14:45:30 +0100 Subject: [PATCH 086/342] switch from using leading zeros to trailing zeros --- src/tools/rust-analyzer/crates/ide/src/hover/render.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 95be7c5618ef..fb7c2904afc5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1235,9 +1235,12 @@ fn is_pwr2plus1(val: u128) -> bool { val != 0 && is_pwr2(val - 1) } +/// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power +/// of 2, or this function will panic. fn pwr2_to_exponent(num: u128) -> String { const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; - (127 - num.leading_zeros()) + assert_eq!(num.count_ones(), 1); + num.trailing_zeros() .to_string() .chars() .map(|c| c.to_digit(10).unwrap() as usize) From d8553c9c056d8f8f206dc3910e6c6256302b7e35 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Tue, 21 Jan 2025 14:47:07 +0100 Subject: [PATCH 087/342] remove is_pwr2 --- .../crates/ide/src/hover/render.rs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index fb7c2904afc5..40f3406b72d3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1083,7 +1083,7 @@ fn render_memory_layout( if config.niches { if let Some(niches) = layout.niches() { if niches > 1024 { - if is_pwr2(niches) { + if niches.is_power_of_two() { format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); } else if is_pwr2plus1(niches) { format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); @@ -1223,16 +1223,12 @@ fn render_dyn_compatibility( } } -fn is_pwr2(val: u128) -> bool { - val.is_power_of_two() -} - fn is_pwr2minus1(val: u128) -> bool { - val == u128::MAX || is_pwr2(val + 1) + val == u128::MAX || (val + 1).is_power_of_two() } fn is_pwr2plus1(val: u128) -> bool { - val != 0 && is_pwr2(val - 1) + val != 0 && (val - 1).is_power_of_two() } /// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power @@ -1254,16 +1250,6 @@ mod tests { const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX]; - #[test] - fn test_is_pwr2() { - const OUTCOMES: [bool; 10] = - [false, true, true, false, true, false, true, false, false, false]; - for (test, expected) in TESTERS.iter().zip(OUTCOMES) { - let actual = is_pwr2(*test); - assert_eq!(actual, expected, "is_pwr2({test}) gave {actual}, expected {expected}"); - } - } - #[test] fn test_is_pwr2minus1() { const OUTCOMES: [bool; 10] = From 759212cd598433595c91b8b17f701faf3934f263 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 14 Dec 2024 09:13:12 +0100 Subject: [PATCH 088/342] remove support for the #[start] attribute --- tests/ui/borrow_as_ptr_no_std.fixed | 13 ++------ tests/ui/borrow_as_ptr_no_std.rs | 13 ++------ tests/ui/borrow_as_ptr_no_std.stderr | 4 +-- tests/ui/box_default_no_std.rs | 13 ++------ tests/ui/crashes/ice-7410.rs | 13 ++------ .../no_std_main_recursion.rs | 32 ------------------- tests/ui/crate_level_checks/no_std_swap.fixed | 3 +- tests/ui/crate_level_checks/no_std_swap.rs | 3 +- .../ui/crate_level_checks/no_std_swap.stderr | 2 +- tests/ui/def_id_nocore.rs | 2 +- tests/ui/empty_loop_no_std.rs | 20 ++---------- tests/ui/empty_loop_no_std.stderr | 12 ++----- tests/ui/floating_point_arithmetic_nostd.rs | 13 ++------ .../ui/missing_const_for_fn/cant_be_const.rs | 10 ------ tests/ui/missing_spin_loop_no_std.fixed | 13 ++------ tests/ui/missing_spin_loop_no_std.rs | 13 ++------ tests/ui/missing_spin_loop_no_std.stderr | 2 +- tests/ui/result_unit_error_no_std.rs | 7 ++-- tests/ui/result_unit_error_no_std.stderr | 2 +- tests/ui/zero_ptr_no_std.fixed | 13 ++------ tests/ui/zero_ptr_no_std.rs | 13 ++------ tests/ui/zero_ptr_no_std.stderr | 6 ++-- 22 files changed, 37 insertions(+), 185 deletions(-) delete mode 100644 tests/ui/crate_level_checks/no_std_main_recursion.rs diff --git a/tests/ui/borrow_as_ptr_no_std.fixed b/tests/ui/borrow_as_ptr_no_std.fixed index f66554de3000..26c6a5033d16 100644 --- a/tests/ui/borrow_as_ptr_no_std.fixed +++ b/tests/ui/borrow_as_ptr_no_std.fixed @@ -1,10 +1,9 @@ #![warn(clippy::borrow_as_ptr)] -#![feature(lang_items, start, libc)] #![no_std] +#![crate_type = "lib"] #[clippy::msrv = "1.75"] -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { let val = 1; let _p = core::ptr::addr_of!(val); @@ -12,11 +11,3 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize { let _p_mut = core::ptr::addr_of_mut!(val_mut); 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/borrow_as_ptr_no_std.rs b/tests/ui/borrow_as_ptr_no_std.rs index 1fc254aafa77..d8d8b4c380ce 100644 --- a/tests/ui/borrow_as_ptr_no_std.rs +++ b/tests/ui/borrow_as_ptr_no_std.rs @@ -1,10 +1,9 @@ #![warn(clippy::borrow_as_ptr)] -#![feature(lang_items, start, libc)] #![no_std] +#![crate_type = "lib"] #[clippy::msrv = "1.75"] -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { let val = 1; let _p = &val as *const i32; @@ -12,11 +11,3 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize { let _p_mut = &mut val_mut as *mut i32; 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/borrow_as_ptr_no_std.stderr b/tests/ui/borrow_as_ptr_no_std.stderr index 6802c86ec95a..488e0bd96776 100644 --- a/tests/ui/borrow_as_ptr_no_std.stderr +++ b/tests/ui/borrow_as_ptr_no_std.stderr @@ -1,5 +1,5 @@ error: borrow as raw pointer - --> tests/ui/borrow_as_ptr_no_std.rs:9:14 + --> tests/ui/borrow_as_ptr_no_std.rs:8:14 | LL | let _p = &val as *const i32; | ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)` @@ -8,7 +8,7 @@ LL | let _p = &val as *const i32; = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]` error: borrow as raw pointer - --> tests/ui/borrow_as_ptr_no_std.rs:12:18 + --> tests/ui/borrow_as_ptr_no_std.rs:11:18 | LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)` diff --git a/tests/ui/box_default_no_std.rs b/tests/ui/box_default_no_std.rs index 4326abc9a541..edb701fcd084 100644 --- a/tests/ui/box_default_no_std.rs +++ b/tests/ui/box_default_no_std.rs @@ -1,6 +1,6 @@ -#![feature(lang_items, start, libc)] #![warn(clippy::box_default)] #![no_std] +#![crate_type = "lib"] pub struct NotBox { _value: T, @@ -18,16 +18,7 @@ impl Default for NotBox { } } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { let _p = NotBox::new(isize::default()); 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/crashes/ice-7410.rs b/tests/ui/crashes/ice-7410.rs index ccf6d7ff94f2..addbca54e80a 100644 --- a/tests/ui/crashes/ice-7410.rs +++ b/tests/ui/crashes/ice-7410.rs @@ -1,7 +1,7 @@ //@compile-flags: -Clink-arg=-nostartfiles //@ignore-target: apple windows -#![feature(lang_items, start, libc)] +#![crate_type = "lib"] #![no_std] #![allow(clippy::if_same_then_else)] #![allow(clippy::redundant_pattern_matching)] @@ -15,18 +15,9 @@ impl Drop for S { fn drop(&mut self) {} } -#[start] -fn main(argc: isize, argv: *const *const u8) -> isize { +pub fn main(argc: isize, argv: *const *const u8) -> isize { if let Some(_) = Some(S) { } else { } 0 } - -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs deleted file mode 100644 index 9e5b2a489034..000000000000 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target: apple - -#![feature(lang_items, start, libc)] -#![no_std] - -use core::panic::PanicInfo; -use core::sync::atomic::{AtomicUsize, Ordering}; - -static N: AtomicUsize = AtomicUsize::new(0); - -#[warn(clippy::main_recursion)] -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - let x = N.load(Ordering::Relaxed); - N.store(x + 1, Ordering::Relaxed); - - if x < 3 { - main(_argc, _argv); - } - - 0 -} - -#[allow(clippy::empty_loop)] -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/crate_level_checks/no_std_swap.fixed b/tests/ui/crate_level_checks/no_std_swap.fixed index 32bccd3a0ffc..e09a913ef06c 100644 --- a/tests/ui/crate_level_checks/no_std_swap.fixed +++ b/tests/ui/crate_level_checks/no_std_swap.fixed @@ -1,11 +1,10 @@ #![no_std] -#![feature(lang_items, start, libc)] #![crate_type = "lib"] use core::panic::PanicInfo; #[warn(clippy::all)] -fn main() { +pub fn main() { let mut a = 42; let mut b = 1337; diff --git a/tests/ui/crate_level_checks/no_std_swap.rs b/tests/ui/crate_level_checks/no_std_swap.rs index 8ed45a334655..536e71b4a25a 100644 --- a/tests/ui/crate_level_checks/no_std_swap.rs +++ b/tests/ui/crate_level_checks/no_std_swap.rs @@ -1,11 +1,10 @@ #![no_std] -#![feature(lang_items, start, libc)] #![crate_type = "lib"] use core::panic::PanicInfo; #[warn(clippy::all)] -fn main() { +pub fn main() { let mut a = 42; let mut b = 1337; diff --git a/tests/ui/crate_level_checks/no_std_swap.stderr b/tests/ui/crate_level_checks/no_std_swap.stderr index bcc8684f7c2b..3e37bd95ef34 100644 --- a/tests/ui/crate_level_checks/no_std_swap.stderr +++ b/tests/ui/crate_level_checks/no_std_swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are trying to swap `a` and `b` - --> tests/ui/crate_level_checks/no_std_swap.rs:12:5 + --> tests/ui/crate_level_checks/no_std_swap.rs:11:5 | LL | / a = b; ... | diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index c9650312db87..03f5ca31f5f0 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -1,6 +1,6 @@ //@ignore-target: apple -#![feature(no_core, lang_items, start)] +#![feature(no_core, lang_items)] #![no_core] #![allow(clippy::missing_safety_doc)] diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs index 1bb895bda75d..9bfcbfba9697 100644 --- a/tests/ui/empty_loop_no_std.rs +++ b/tests/ui/empty_loop_no_std.rs @@ -2,27 +2,11 @@ //@ignore-target: apple #![warn(clippy::empty_loop)] -#![feature(lang_items, start, libc)] +#![crate_type = "lib"] #![no_std] -use core::panic::PanicInfo; - -#[start] -fn main(argc: isize, argv: *const *const u8) -> isize { +pub fn main(argc: isize, argv: *const *const u8) -> isize { // This should trigger the lint loop {} //~^ ERROR: empty `loop {}` wastes CPU cycles } - -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - // This should NOT trigger the lint - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() { - // This should also trigger the lint - loop {} - //~^ ERROR: empty `loop {}` wastes CPU cycles -} diff --git a/tests/ui/empty_loop_no_std.stderr b/tests/ui/empty_loop_no_std.stderr index f4a18204c3ce..f36fb9d9e3f2 100644 --- a/tests/ui/empty_loop_no_std.stderr +++ b/tests/ui/empty_loop_no_std.stderr @@ -1,5 +1,5 @@ error: empty `loop {}` wastes CPU cycles - --> tests/ui/empty_loop_no_std.rs:13:5 + --> tests/ui/empty_loop_no_std.rs:10:5 | LL | loop {} | ^^^^^^^ @@ -8,13 +8,5 @@ LL | loop {} = note: `-D clippy::empty-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` -error: empty `loop {}` wastes CPU cycles - --> tests/ui/empty_loop_no_std.rs:26:5 - | -LL | loop {} - | ^^^^^^^ - | - = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/floating_point_arithmetic_nostd.rs b/tests/ui/floating_point_arithmetic_nostd.rs index 8ea75fae89b6..81e4e0380dad 100644 --- a/tests/ui/floating_point_arithmetic_nostd.rs +++ b/tests/ui/floating_point_arithmetic_nostd.rs @@ -1,4 +1,4 @@ -#![feature(lang_items, start)] +#![crate_type = "lib"] #![warn(clippy::imprecise_flops)] #![warn(clippy::suboptimal_flops)] #![no_std] @@ -17,15 +17,6 @@ fn fake_abs1(num: f64) -> f64 { if num >= 0.0 { num } else { -num } } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index d2f9e34a5ceb..fdde68790a8c 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -6,7 +6,6 @@ //@aux-build:../auxiliary/proc_macros.rs #![warn(clippy::missing_const_for_fn)] -#![feature(start)] #![feature(type_alias_impl_trait)] extern crate helper; @@ -71,15 +70,6 @@ mod with_test_fn { } } -// Allowing on this function, because it would lint, which we don't want in this case. -// if we have `#[start]` and `#[test]` check `is_entrypoint_fn(cx, def_id.to_def_id())` is stopped -// working -#[allow(clippy::missing_const_for_fn)] -#[start] -fn init(num: isize, something: *const *const u8) -> isize { - 1 -} - trait Foo { // This should not be suggested to be made const // (rustc doesn't allow const trait methods) diff --git a/tests/ui/missing_spin_loop_no_std.fixed b/tests/ui/missing_spin_loop_no_std.fixed index 497e0e243174..771ab1ab21a8 100644 --- a/tests/ui/missing_spin_loop_no_std.fixed +++ b/tests/ui/missing_spin_loop_no_std.fixed @@ -1,22 +1,13 @@ #![warn(clippy::missing_spin_loop)] -#![feature(lang_items, start, libc)] +#![crate_type = "lib"] #![no_std] use core::sync::atomic::{AtomicBool, Ordering}; -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { // This should trigger the lint let b = AtomicBool::new(true); // This should lint with `core::hint::spin_loop()` while b.load(Ordering::Acquire) { core::hint::spin_loop() } 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/missing_spin_loop_no_std.rs b/tests/ui/missing_spin_loop_no_std.rs index 1c85a9c58d65..bf890fc4066b 100644 --- a/tests/ui/missing_spin_loop_no_std.rs +++ b/tests/ui/missing_spin_loop_no_std.rs @@ -1,22 +1,13 @@ #![warn(clippy::missing_spin_loop)] -#![feature(lang_items, start, libc)] +#![crate_type = "lib"] #![no_std] use core::sync::atomic::{AtomicBool, Ordering}; -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { // This should trigger the lint let b = AtomicBool::new(true); // This should lint with `core::hint::spin_loop()` while b.load(Ordering::Acquire) {} 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/missing_spin_loop_no_std.stderr b/tests/ui/missing_spin_loop_no_std.stderr index 7911620d32c5..d4b9485be461 100644 --- a/tests/ui/missing_spin_loop_no_std.stderr +++ b/tests/ui/missing_spin_loop_no_std.stderr @@ -1,5 +1,5 @@ error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop_no_std.rs:12:37 + --> tests/ui/missing_spin_loop_no_std.rs:11:37 | LL | while b.load(Ordering::Acquire) {} | ^^ help: try: `{ core::hint::spin_loop() }` diff --git a/tests/ui/result_unit_error_no_std.rs b/tests/ui/result_unit_error_no_std.rs index 1e7a028a7fc0..c9f4996c3689 100644 --- a/tests/ui/result_unit_error_no_std.rs +++ b/tests/ui/result_unit_error_no_std.rs @@ -1,5 +1,6 @@ -#![feature(lang_items, start, libc)] +#![feature(lang_items, libc)] #![no_std] +#![no_main] #![warn(clippy::result_unit_err)] #[clippy::msrv = "1.80"] @@ -12,8 +13,8 @@ pub fn returns_unit_error_lint() -> Result { Err(()) } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { 0 } diff --git a/tests/ui/result_unit_error_no_std.stderr b/tests/ui/result_unit_error_no_std.stderr index 33692e605543..a7807f089ab2 100644 --- a/tests/ui/result_unit_error_no_std.stderr +++ b/tests/ui/result_unit_error_no_std.stderr @@ -1,5 +1,5 @@ error: this returns a `Result<_, ()>` - --> tests/ui/result_unit_error_no_std.rs:11:1 + --> tests/ui/result_unit_error_no_std.rs:12:1 | LL | pub fn returns_unit_error_lint() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/zero_ptr_no_std.fixed b/tests/ui/zero_ptr_no_std.fixed index 4f4d19e883d1..25143eee8cc3 100644 --- a/tests/ui/zero_ptr_no_std.fixed +++ b/tests/ui/zero_ptr_no_std.fixed @@ -1,19 +1,10 @@ -#![feature(lang_items, start, libc)] +#![crate_type = "lib"] #![no_std] #![deny(clippy::zero_ptr)] -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { let _ = core::ptr::null::(); let _ = core::ptr::null_mut::(); let _: *const u8 = core::ptr::null(); 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.rs b/tests/ui/zero_ptr_no_std.rs index 54954d8d13fe..965733b45d92 100644 --- a/tests/ui/zero_ptr_no_std.rs +++ b/tests/ui/zero_ptr_no_std.rs @@ -1,19 +1,10 @@ -#![feature(lang_items, start, libc)] +#![crate_type = "lib"] #![no_std] #![deny(clippy::zero_ptr)] -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { let _ = 0 as *const usize; let _ = 0 as *mut f64; let _: *const u8 = 0 as *const _; 0 } - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.stderr b/tests/ui/zero_ptr_no_std.stderr index 42a1a41ca94f..014bf312bf32 100644 --- a/tests/ui/zero_ptr_no_std.stderr +++ b/tests/ui/zero_ptr_no_std.stderr @@ -1,5 +1,5 @@ error: `0 as *const _` detected - --> tests/ui/zero_ptr_no_std.rs:7:13 + --> tests/ui/zero_ptr_no_std.rs:6:13 | LL | let _ = 0 as *const usize; | ^^^^^^^^^^^^^^^^^ help: try: `core::ptr::null::()` @@ -11,13 +11,13 @@ LL | #![deny(clippy::zero_ptr)] | ^^^^^^^^^^^^^^^^ error: `0 as *mut _` detected - --> tests/ui/zero_ptr_no_std.rs:8:13 + --> tests/ui/zero_ptr_no_std.rs:7:13 | LL | let _ = 0 as *mut f64; | ^^^^^^^^^^^^^ help: try: `core::ptr::null_mut::()` error: `0 as *const _` detected - --> tests/ui/zero_ptr_no_std.rs:9:24 + --> tests/ui/zero_ptr_no_std.rs:8:24 | LL | let _: *const u8 = 0 as *const _; | ^^^^^^^^^^^^^ help: try: `core::ptr::null()` From f5567e1132c1dce184f34313715e3f22dc6a8e04 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 21 Jan 2025 02:19:27 -0800 Subject: [PATCH 089/342] organize old well-typed-edition-2024 tests This doesn't (or at least shouldn't!) add, remove, or change any test cases. I've grouped them by which rule variants they test. --- .../experimental/well-typed-edition-2024.rs | 77 ++++---- .../well-typed-edition-2024.stable2021.stderr | 168 +++++++++--------- 2 files changed, 130 insertions(+), 115 deletions(-) diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs index a8ad014fc5d3..3114b9d3bf8c 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -11,20 +11,34 @@ #![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] pub fn main() { - if let Some(Some(&x)) = &Some(&Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected integer, found `&_` - let _: u32 = x; + // Tests not using match ergonomics. These should always succeed with the same bindings. + if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { + let _: &u32 = x; } + + // Tests for differences in how many layers of reference are eaten by reference patterns if let Some(Some(&x)) = &Some(Some(&0)) { #[cfg(stable2021)] let _: u32 = x; #[cfg(any(classic2024, structural2024))] let _: &u32 = x; } + if let Some(&Some(x)) = &mut Some(&Some(0)) { + // This additionally tests that `&` patterns can eat inherited `&mut` refs. + // This is possible on stable when the real reference being eaten is of a `&` type. + #[cfg(stable2021)] let _: u32 = x; + #[cfg(any(classic2024, structural2024))] let _: &u32 = x; + } if let Some(Some(&&x)) = &Some(Some(&0)) { //[stable2021]~^ mismatched types //[stable2021]~| expected integer, found `&_` let _: u32 = x; } + + // Tests for eating a lone inherited reference + if let Some(Some(&x)) = &Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } if let Some(&Some(x)) = &Some(Some(0)) { //[stable2021]~^ mismatched types //[stable2021]~| expected `Option<{integer}>`, found `&_` @@ -35,44 +49,45 @@ pub fn main() { //[stable2021]~| expected integer, found `&mut _` let _: u32 = x; } - if let Some(&Some(&x)) = &mut Some(&Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected integer, found `&_` - let _: u32 = x; - } - if let Some(&Some(x)) = &mut Some(&Some(0)) { - #[cfg(stable2021)] let _: u32 = x; - #[cfg(any(classic2024, structural2024))] let _: &u32 = x; - } - if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { - let _: &u32 = x; - } - if let Some(&Some(&x)) = &Some(&mut Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| types differ in mutability - let _: u32 = x; - } - if let Some(&Some(&x)) = &Some(&Some(0)) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected integer, found `&_` - let _: u32 = x; - } - if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { - //[stable2021]~^ mismatched types - //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` - let _: u32 = x; - } + + // Tests for `&` patterns matching real `&mut` reference types if let Some(&Some(&x)) = Some(&Some(&mut 0)) { //[stable2021]~^ mismatched types //[stable2021]~| types differ in mutability let _: u32 = x; } + + // Tests for eating only one layer and also eating a lone inherited reference + if let Some(&Some(&x)) = &Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } + + // Tests for `&` matching a lone inherited possibly-`&mut` reference + if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` + let _: u32 = x; + } if let Some(&Some(x)) = &mut Some(Some(0)) { //[stable2021]~^ mismatched types //[stable2021]~| expected `Option<{integer}>`, found `&_` let _: u32 = x; } + // Tests eating one layer, eating a lone inherited ref, and `&` eating `&mut` (realness varies) + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: u32 = x; + } + if let Some(&Some(&x)) = &mut Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } + // Tests for eat-inner rulesets matching on the outer reference if matching on the inner // reference causes a mutability mismatch, i.e. `Deref(EatInner, FallbackToOuter)`: let [&mut x] = &mut [&0]; diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr index c78459bb21cf..e9c338de2431 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr @@ -1,20 +1,5 @@ error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:14:22 - | -LL | if let Some(Some(&x)) = &Some(&Some(0)) { - | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(Some(x)) = &Some(&Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:23:23 + --> $DIR/well-typed-edition-2024.rs:30:23 | LL | if let Some(Some(&&x)) = &Some(Some(&0)) { | ^^ --------------- this expression has type `&Option>` @@ -30,7 +15,22 @@ LL + if let Some(Some(&x)) = &Some(Some(&0)) { | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:28:17 + --> $DIR/well-typed-edition-2024.rs:37:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:42:17 | LL | if let Some(&Some(x)) = &Some(Some(0)) { | ^^^^^^^^ -------------- this expression has type `&Option>` @@ -41,7 +41,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:33:22 + --> $DIR/well-typed-edition-2024.rs:47:22 | LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` @@ -51,7 +51,7 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { = note: expected type `{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:33:22 + --> $DIR/well-typed-edition-2024.rs:47:22 | LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ @@ -61,59 +61,7 @@ LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { | ~ error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:38:23 - | -LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { - | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:50:17 - | -LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { - | ^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` - | | - | types differ in mutability - | - = note: expected mutable reference `&mut Option<{integer}>` - found reference `&_` - -error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:55:23 - | -LL | if let Some(&Some(&x)) = &Some(&Some(0)) { - | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | expected integer, found `&_` - | - = note: expected type `{integer}` - found reference `&_` -help: consider removing `&` from the pattern - | -LL | if let Some(&Some(x)) = &Some(&Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:60:17 - | -LL | if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { - | ^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` - | | - | expected `Option<&mut Option<{integer}>>`, found `&_` - | - = note: expected enum `Option<&mut Option<{integer}>>` - found reference `&_` - -error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:65:23 + --> $DIR/well-typed-edition-2024.rs:54:23 | LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` @@ -128,7 +76,33 @@ LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { | ~ error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:70:17 + --> $DIR/well-typed-edition-2024.rs:61:23 + | +LL | if let Some(&Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = &Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:68:17 + | +LL | if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + | ^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected `Option<&mut Option<{integer}>>`, found `&_` + | + = note: expected enum `Option<&mut Option<{integer}>>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:73:17 | LL | if let Some(&Some(x)) = &mut Some(Some(0)) { | ^^^^^^^^ ------------------ this expression has type `&mut Option>` @@ -139,7 +113,33 @@ LL | if let Some(&Some(x)) = &mut Some(Some(0)) { found reference `&_` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:78:10 + --> $DIR/well-typed-edition-2024.rs:80:17 + | +LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { + | ^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:85:23 + | +LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { + | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { + | ~ + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:93:10 | LL | let [&mut x] = &mut [&0]; | ^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -149,7 +149,7 @@ LL | let [&mut x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:78:10 + --> $DIR/well-typed-edition-2024.rs:93:10 | LL | let [&mut x] = &mut [&0]; | ^^^^^^ @@ -160,7 +160,7 @@ LL + let [x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:83:10 + --> $DIR/well-typed-edition-2024.rs:98:10 | LL | let [&mut ref x] = &mut [&0]; | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -170,7 +170,7 @@ LL | let [&mut ref x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:83:10 + --> $DIR/well-typed-edition-2024.rs:98:10 | LL | let [&mut ref x] = &mut [&0]; | ^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let [ref x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:88:10 + --> $DIR/well-typed-edition-2024.rs:103:10 | LL | let [&mut ref mut x] = &mut [&0]; | ^^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -191,7 +191,7 @@ LL | let [&mut ref mut x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:88:10 + --> $DIR/well-typed-edition-2024.rs:103:10 | LL | let [&mut ref mut x] = &mut [&0]; | ^^^^^^^^^^^^^^ @@ -202,7 +202,7 @@ LL + let [ref mut x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:93:10 + --> $DIR/well-typed-edition-2024.rs:108:10 | LL | let [&mut mut x] = &mut [&0]; | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -212,7 +212,7 @@ LL | let [&mut mut x] = &mut [&0]; = note: expected reference `&{integer}` found mutable reference `&mut _` note: to declare a mutable binding use: `mut x` - --> $DIR/well-typed-edition-2024.rs:93:10 + --> $DIR/well-typed-edition-2024.rs:108:10 | LL | let [&mut mut x] = &mut [&0]; | ^^^^^^^^^^ @@ -223,7 +223,7 @@ LL + let [mut x] = &mut [&0]; | error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:98:10 + --> $DIR/well-typed-edition-2024.rs:113:10 | LL | let [&mut &x] = &mut [&0]; | ^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -234,7 +234,7 @@ LL | let [&mut &x] = &mut [&0]; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:103:10 + --> $DIR/well-typed-edition-2024.rs:118:10 | LL | let [&mut &ref x] = &mut [&0]; | ^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` @@ -245,7 +245,7 @@ LL | let [&mut &ref x] = &mut [&0]; found mutable reference `&mut _` error[E0308]: mismatched types - --> $DIR/well-typed-edition-2024.rs:108:10 + --> $DIR/well-typed-edition-2024.rs:123:10 | LL | let [&mut &(mut x)] = &mut [&0]; | ^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` From e288cff5cf38c66a5fad52d010e97f9dd3c5b991 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 21 Jan 2025 06:27:11 -0800 Subject: [PATCH 090/342] add tests differing between stable and new rules (with errors on new rules) Since there are so many ways to write these, I've opted to only include two sorts of test: simple tests that directly target the rules differing between rulesets and nuanced tests that produce different errors under different rulesets. I've also tried not to add any duplicate tests. `well-typed-edition-2024.rs` already has tests disagreeing with stable, so I've opted not to include any in this commit that are well-typed under the new rules. --- .../mut-ref-mut.classic2024.stderr | 17 +++- .../experimental/mut-ref-mut.rs | 6 ++ .../mut-ref-mut.structural2024.stderr | 12 ++- .../ref-binding-on-inh-ref-errors.rs | 44 ++++++++-- ...inding-on-inh-ref-errors.stable2021.stderr | 15 +++- ...ng-on-inh-ref-errors.structural2024.stderr | 81 +++++++++++++++++-- 6 files changed, 156 insertions(+), 19 deletions(-) diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr index 3849b4ed9894..afaa925a7577 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr @@ -18,6 +18,19 @@ LL | let Foo(mut a) = &mut Foo(0); = help: add `#![feature(mut_ref)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/mut-ref-mut.rs:24:10 + | +LL | let [&mut mut x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL | let [&mut x] = &[&mut 0]; + | ~ -For more information about this error, try `rustc --explain E0658`. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs index 68d6871eabfa..fbd6514df73d 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs @@ -20,4 +20,10 @@ pub fn main() { //[classic2024,structural2024]~^ ERROR: binding cannot be both mutable and by-reference #[cfg(stable2021)] { a = 42 } #[cfg(any(classic2024, structural2024))] { a = &mut 42 } + + let [&mut mut x] = &[&mut 0]; + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^^^ binding cannot be both mutable and by-reference + #[cfg(stable2021)] { x = 0 } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr index 3849b4ed9894..fd82da70a18d 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr @@ -18,6 +18,16 @@ LL | let Foo(mut a) = &mut Foo(0); = help: add `#![feature(mut_ref)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 2 previous errors +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/mut-ref-mut.rs:24:15 + | +LL | let [&mut mut x] = &[&mut 0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs index a151c84739c1..4c88c0c63ae5 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -2,7 +2,6 @@ //@[stable2021] edition: 2021 //@[classic2024] edition: 2024 //@[structural2024] edition: 2024 -//@[classic2024] run-pass //! Tests for errors from binding with `ref x` under a by-ref default binding mode in edition 2024. //! These can't be in the same body as tests for other errors, since they're emitted during THIR //! construction. The errors on stable edition 2021 Rust are unrelated. @@ -36,8 +35,6 @@ fn errors_from_eating_the_real_reference() { //[structural2024]~| cannot override to bind by-reference when that is the implicit default #[cfg(stable2021)] let _: &mut u32 = x; #[cfg(classic2024)] let _: &mut &mut u32 = x; - - errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable(); } /// To make absolutely sure binding with `ref` ignores inherited references on stable, let's @@ -58,6 +55,43 @@ fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() { #[cfg(classic2024)] let _: &&mut u32 = x; } -pub fn main() { - errors_from_eating_the_real_reference(); +/// This one also needs to be quarantined for a typeck error on `classic2024` (eat-outer). +fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() { + let [&mut ref x] = &[&mut 0]; + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^^^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &u32 = x; +} + +/// These should be errors in all editions. In edition 2024, they should be caught by the pattern +/// typing rules disallowing `ref` when there's an inherited reference. In old editions where that +/// resets the binding mode, they're borrowck errors due to binding with `ref mut`. +/// As a quirk of how the edition 2024 error is emitted during THIR construction, it ends up going +/// through borrowck as well, using the old `ref` behavior as a fallback, so we get that error too. +fn borrowck_errors_in_old_editions() { + let [ref mut x] = &[0]; + //~^ ERROR: cannot borrow data in a `&` reference as mutable + //[classic2024,structural2024]~| ERROR: this pattern relies on behavior which may change in edition 2024 + //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default +} + +/// The remaining tests are purely for testing `ref` bindings in the presence of an inherited +/// reference. These should always fail on edition 2024 and succeed on edition 2021. +pub fn main() { + let [ref x] = &[0]; + //[classic2024,structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &u32 = x; + + let [ref x] = &mut [0]; + //[classic2024,structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &u32 = x; + + let [ref mut x] = &mut [0]; + //[classic2024,structural2024]~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //[classic2024,structural2024]~| cannot override to bind by-reference when that is the implicit default + #[cfg(stable2021)] let _: &mut u32 = x; } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr index c0518130368d..a21e4bb5b8f8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/ref-binding-on-inh-ref-errors.rs:46:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:43:10 | LL | let [&ref x] = &[&mut 0]; | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` @@ -15,7 +15,7 @@ LL + let [ref x] = &[&mut 0]; | error[E0308]: mismatched types - --> $DIR/ref-binding-on-inh-ref-errors.rs:53:10 + --> $DIR/ref-binding-on-inh-ref-errors.rs:50:10 | LL | let [&ref x] = &mut [&mut 0]; | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` @@ -30,6 +30,13 @@ LL - let [&ref x] = &mut [&mut 0]; LL + let [ref x] = &mut [&mut 0]; | -error: aborting due to 2 previous errors +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^ cannot borrow as mutable -For more information about this error, try `rustc --explain E0308`. +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr index 92aa8f1ea685..ee2c831bfcc8 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr @@ -1,5 +1,5 @@ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:16:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:15:11 | LL | let [&ref x] = &[&0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -11,7 +11,7 @@ LL | let &[&ref x] = &[&0]; | + error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:22:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:21:11 | LL | let [&ref x] = &mut [&0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -23,7 +23,7 @@ LL | let &mut [&ref x] = &mut [&0]; | ++++ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:28:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:27:15 | LL | let [&mut ref x] = &mut [&mut 0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -35,7 +35,7 @@ LL | let &mut [&mut ref x] = &mut [&mut 0]; | ++++ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:34:15 + --> $DIR/ref-binding-on-inh-ref-errors.rs:33:15 | LL | let [&mut ref mut x] = &mut [&mut 0]; | ^^^^^^^ cannot override to bind by-reference when that is the implicit default @@ -47,7 +47,7 @@ LL | let &mut [&mut ref mut x] = &mut [&mut 0]; | ++++ error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:46:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:43:11 | LL | let [&ref x] = &[&mut 0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -59,7 +59,7 @@ LL | let &[&ref x] = &[&mut 0]; | + error: this pattern relies on behavior which may change in edition 2024 - --> $DIR/ref-binding-on-inh-ref-errors.rs:53:11 + --> $DIR/ref-binding-on-inh-ref-errors.rs:50:11 | LL | let [&ref x] = &mut [&mut 0]; | ^^^ cannot override to bind by-reference when that is the implicit default @@ -70,5 +70,72 @@ help: make the implied reference pattern explicit LL | let &mut [&ref x] = &mut [&mut 0]; | ++++ -error: aborting due to 6 previous errors +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:60:15 + | +LL | let [&mut ref x] = &[&mut 0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[&mut ref x] = &[&mut 0]; + | + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[ref mut x] = &[0]; + | + + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref-binding-on-inh-ref-errors.rs:74:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^ cannot borrow as mutable + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10 + | +LL | let [ref x] = &[0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &[ref x] = &[0]; + | + + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:88:10 + | +LL | let [ref x] = &mut [0]; + | ^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [ref x] = &mut [0]; + | ++++ + +error: this pattern relies on behavior which may change in edition 2024 + --> $DIR/ref-binding-on-inh-ref-errors.rs:93:10 + | +LL | let [ref mut x] = &mut [0]; + | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | + = note: for more information, see +help: make the implied reference pattern explicit + | +LL | let &mut [ref mut x] = &mut [0]; + | ++++ + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0596`. From 284e0cd74945d05515bd2d257ae814be9bba91d5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 12:07:50 +0100 Subject: [PATCH 091/342] Make `InlayHint::linked_location` computation lazy --- .../crates/ide/src/inlay_hints.rs | 22 +- .../crates/ide/src/inlay_hints/bounds.rs | 32 +- .../crates/ide/src/inlay_hints/chaining.rs | 301 ++++++++++-------- .../ide/src/inlay_hints/closing_brace.rs | 6 +- .../ide/src/inlay_hints/closure_captures.rs | 17 +- .../ide/src/inlay_hints/generic_param.rs | 53 +-- .../ide/src/inlay_hints/implicit_drop.rs | 29 +- .../crates/ide/src/inlay_hints/param_name.rs | 24 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +- 9 files changed, 285 insertions(+), 204 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 94dd90c4aac0..405cbf607840 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -300,6 +300,7 @@ pub struct InlayHintsConfig { pub closing_brace_hints_min_lines: Option, pub fields_to_resolve: InlayFieldsToResolve, } + impl InlayHintsConfig { fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy { if self.fields_to_resolve.resolve_text_edits { @@ -329,6 +330,19 @@ impl InlayHintsConfig { Lazy::Computed(tooltip) } } + + /// This always reports a resolvable location, so only use this when it is very likely for a + /// location link to actually resolve but where computing `finish` would be costly. + fn lazy_location_opt( + &self, + finish: impl FnOnce() -> Option, + ) -> Option> { + if self.fields_to_resolve.resolve_label_location { + Some(Lazy::Lazy) + } else { + finish().map(Lazy::Computed) + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -509,7 +523,7 @@ impl InlayHintLabel { pub fn simple( s: impl Into, tooltip: Option>, - linked_location: Option, + linked_location: Option>, ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], @@ -593,7 +607,7 @@ pub struct InlayHintLabelPart { /// refers to (not necessarily the location itself). /// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// them both. - pub linked_location: Option, + pub linked_location: Option>, /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// hover requests to show. pub tooltip: Option>, @@ -602,7 +616,7 @@ pub struct InlayHintLabelPart { impl std::hash::Hash for InlayHintLabelPart { fn hash(&self, state: &mut H) { self.text.hash(state); - self.linked_location.hash(state); + self.linked_location.is_some().hash(state); self.tooltip.is_some().hash(state); } } @@ -663,7 +677,7 @@ impl InlayHintLabelBuilder<'_> { if !text.is_empty() { self.result.parts.push(InlayHintLabelPart { text, - linked_location: self.location.take(), + linked_location: self.location.take().map(Lazy::Computed), tooltip: None, }); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index 429ddd31cbd0..e9b728bcaa75 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -22,11 +22,7 @@ pub(super) fn hints( return None; } - let linked_location = - famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| { - let n = it.call_site(); - FileRange { file_id: n.file_id, range: n.focus_or_full_range() } - }); + let sized_trait = famous_defs.core_marker_Sized(); for param in params.type_or_const_params() { match param { @@ -48,7 +44,17 @@ pub(super) fn hints( } hint.parts.push(InlayHintLabelPart { text: "Sized".to_owned(), - linked_location, + linked_location: sized_trait.and_then(|it| { + config.lazy_location_opt(|| { + it.try_to_nav(sema.db).map(|it| { + let n = it.call_site(); + FileRange { + file_id: n.file_id, + range: n.focus_or_full_range(), + } + }) + }) + }), tooltip: None, }); if has_bounds { @@ -134,12 +140,14 @@ fn foo() {} InlayHintLabelPart { text: "Sized", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 135..140, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 135..140, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 7fa7ab1a94d6..477fb5156be6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -81,7 +81,10 @@ mod tests { use crate::{ fixture, - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::{ + tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + Lazy, + }, InlayHintsConfig, }; @@ -99,7 +102,7 @@ mod tests { let (analysis, file_id) = fixture::file(ra_fixture); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { - if let Some(loc) = &mut hint.linked_location { + if let Some(Lazy::Computed(loc)) = &mut hint.linked_location { loc.range = TextRange::empty(TextSize::from(0)); } }); @@ -134,12 +137,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 63..64, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 63..64, + }, + ), ), tooltip: "", }, @@ -151,12 +156,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -213,12 +220,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -230,12 +239,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -276,12 +287,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -293,12 +306,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -340,12 +355,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 23..24, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..24, + }, + ), ), tooltip: "", }, @@ -353,12 +370,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -371,12 +390,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -384,12 +405,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -435,12 +458,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -448,12 +473,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -467,12 +494,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -480,12 +509,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -499,12 +530,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -512,12 +545,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -531,12 +566,14 @@ fn main() { InlayHintLabelPart { text: "MyIter", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -577,12 +614,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -594,12 +633,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -611,12 +652,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -628,12 +671,14 @@ fn main() { InlayHintLabelPart { text: "self", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 42..46, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 42..46, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 90b8be64a46d..64f1f83d3f5c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -11,7 +11,9 @@ use syntax::{ match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::Lazy, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, +}; pub(super) fn hints( acc: &mut Vec, @@ -141,7 +143,7 @@ pub(super) fn hints( acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, - label: InlayHintLabel::simple(label, None, linked_location), + label: InlayHintLabel::simple(label, None, linked_location.map(Lazy::Computed)), text_edit: None, position: InlayHintPosition::After, pad_left: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 906f2acf0c44..3e91618d08e6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -53,10 +53,6 @@ pub(super) fn hints( let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { let local = capture.local(); - let source = local.primary_source(sema.db); - - // force cache the source file, otherwise sema lookup will potentially panic - _ = sema.parse_or_expand(source.file()); let label = format!( "{}{}", @@ -73,8 +69,17 @@ pub(super) fn hints( } hint.label.append_part(InlayHintLabelPart { text: label, - linked_location: source.name().and_then(|name| { - name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) + linked_location: config.lazy_location_opt(|| { + let source = local.primary_source(sema.db); + + // force cache the source file, otherwise sema lookup will potentially panic + _ = sema.parse_or_expand(source.file()); + source.name().and_then(|name| { + name.syntax() + .original_file_range_opt(sema.db) + .map(TupleExt::head) + .map(Into::into) + }) }), tooltip: None, }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 13055757ba6a..762a4c265518 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -47,6 +47,18 @@ pub(crate) fn hints( return None; } + let allowed = match (param, &arg) { + (hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints, + (hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints, + (hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => { + lifetime_hints + } + _ => false, + }; + if !allowed { + return None; + } + let param_name = param.name(sema.db); let should_hide = { @@ -60,34 +72,27 @@ pub(crate) fn hints( let range = sema.original_range_opt(arg.syntax())?.range; - let source_syntax = match param { - hir::GenericParam::TypeParam(it) => { - if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { - return None; - } - sema.source(it.merge()).map(|it| it.value.syntax().clone()) - } - hir::GenericParam::ConstParam(it) => { - if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { - return None; - } - let syntax = sema.source(it.merge())?.value.syntax().clone(); - let const_param = ast::ConstParam::cast(syntax)?; - const_param.name().map(|it| it.syntax().clone()) - } - hir::GenericParam::LifetimeParam(it) => { - if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { - return None; - } - sema.source(it).map(|it| it.value.syntax().clone()) - } - }; - let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); let colon = if config.render_colons { ":" } else { "" }; let label = InlayHintLabel::simple( format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), None, - linked_location.map(Into::into), + config.lazy_location_opt(|| { + let source_syntax = match param { + hir::GenericParam::TypeParam(it) => { + sema.source(it.merge()).map(|it| it.value.syntax().clone()) + } + hir::GenericParam::ConstParam(it) => { + let syntax = sema.source(it.merge())?.value.syntax().clone(); + let const_param = ast::ConstParam::cast(syntax)?; + const_param.name().map(|it| it.syntax().clone()) + } + hir::GenericParam::LifetimeParam(it) => { + sema.source(it).map(|it| it.value.syntax().clone()) + } + }; + let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); + linked_location.map(Into::into) + }), ); Some(InlayHint { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 1358d3722f89..27c7c3d49818 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -49,7 +49,7 @@ pub(super) fn hints( if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { continue; // Arguably only ADTs have significant drop impls } - let Some(binding) = local_to_binding.get(place.local) else { + let Some(&binding_idx) = local_to_binding.get(place.local) else { continue; // Ignore temporary values }; let range = match terminator.span { @@ -91,25 +91,26 @@ pub(super) fn hints( }, MirSpan::Unknown => continue, }; - let binding_source = source_map - .patterns_for_binding(*binding) - .first() - .and_then(|d| source_map.pat_syntax(*d).ok()) - .and_then(|d| { - Some(FileRange { - file_id: d.file_id.file_id()?.into(), - range: d.value.text_range(), - }) - }); - let binding = &hir.bindings[*binding]; + let binding = &hir.bindings[binding_idx]; let name = binding.name.display_no_db(file_id.edition()).to_smolstr(); if name.starts_with(" pat.name(), - Either::Right(param) => match param.pat()? { - ast::Pat::IdentPat(it) => it.name(), - _ => None, - }, - }?; - sema.original_range_opt(name_syntax.syntax()) - })(); - let colon = if config.render_colons { ":" } else { "" }; let label = InlayHintLabel::simple( format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), None, - linked_location.map(Into::into), + config.lazy_location_opt(|| { + let source = sema.source(param)?; + let name_syntax = match source.value.as_ref() { + Either::Left(pat) => pat.name(), + Either::Right(param) => match param.pat()? { + ast::Pat::IdentPat(it) => it.name(), + _ => None, + }, + }?; + sema.original_range_opt(name_syntax.syntax()).map(Into::into) + }), ); InlayHint { range, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index a5516e7f9d4f..4947576f1977 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -684,7 +684,10 @@ fn inlay_hint_label( *something_to_resolve |= part.linked_location.is_some(); None } else { - part.linked_location.map(|range| location(snap, range)).transpose()? + part.linked_location + .and_then(|it| it.computed()) + .map(|range| location(snap, range)) + .transpose()? }; Ok(lsp_types::InlayHintLabelPart { value: part.text, From 802d9d53869031e1c63dca15684381a50ec6370c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 12:32:11 +0100 Subject: [PATCH 092/342] Keep already computed inlay hint properties instead of late resolving them --- .../crates/ide/src/inlay_hints.rs | 48 ++++--- .../crates/ide/src/inlay_hints/chaining.rs | 4 +- .../ide/src/inlay_hints/closing_brace.rs | 4 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 133 +++++++++--------- 5 files changed, 99 insertions(+), 92 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 405cbf607840..088a11bcb4cc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -302,21 +302,21 @@ pub struct InlayHintsConfig { } impl InlayHintsConfig { - fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy { + fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty { if self.fields_to_resolve.resolve_text_edits { - Lazy::Lazy + LazyProperty::Lazy } else { let edit = finish(); never!(edit.is_empty(), "inlay hint produced an empty text edit"); - Lazy::Computed(edit) + LazyProperty::Computed(edit) } } - fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy { + fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty { if self.fields_to_resolve.resolve_hint_tooltip && self.fields_to_resolve.resolve_label_tooltip { - Lazy::Lazy + LazyProperty::Lazy } else { let tooltip = finish(); never!( @@ -327,7 +327,7 @@ impl InlayHintsConfig { .is_empty(), "inlay hint produced an empty tooltip" ); - Lazy::Computed(tooltip) + LazyProperty::Computed(tooltip) } } @@ -336,11 +336,11 @@ impl InlayHintsConfig { fn lazy_location_opt( &self, finish: impl FnOnce() -> Option, - ) -> Option> { + ) -> Option> { if self.fields_to_resolve.resolve_label_location { - Some(Lazy::Lazy) + Some(LazyProperty::Lazy) } else { - finish().map(Lazy::Computed) + finish().map(LazyProperty::Computed) } } } @@ -455,7 +455,7 @@ pub struct InlayHint { /// The actual label to show in the inlay hint. pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. - pub text_edit: Option>, + pub text_edit: Option>, /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the /// hint does not support resolving. pub resolve_parent: Option, @@ -463,15 +463,15 @@ pub struct InlayHint { /// A type signaling that a value is either computed, or is available for computation. #[derive(Clone, Debug)] -pub enum Lazy { +pub enum LazyProperty { Computed(T), Lazy, } -impl Lazy { +impl LazyProperty { pub fn computed(self) -> Option { match self { - Lazy::Computed(it) => Some(it), + LazyProperty::Computed(it) => Some(it), _ => None, } } @@ -522,8 +522,8 @@ pub struct InlayHintLabel { impl InlayHintLabel { pub fn simple( s: impl Into, - tooltip: Option>, - linked_location: Option>, + tooltip: Option>, + linked_location: Option>, ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], @@ -607,10 +607,10 @@ pub struct InlayHintLabelPart { /// refers to (not necessarily the location itself). /// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// them both. - pub linked_location: Option>, + pub linked_location: Option>, /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// hover requests to show. - pub tooltip: Option>, + pub tooltip: Option>, } impl std::hash::Hash for InlayHintLabelPart { @@ -624,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart { impl fmt::Debug for InlayHintLabelPart { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f), + Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => { + text.fmt(f) + } Self { text, linked_location, tooltip } => f .debug_struct("InlayHintLabelPart") .field("text", text) @@ -632,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart { .field( "tooltip", &tooltip.as_ref().map_or("", |it| match it { - Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it, - Lazy::Lazy => "", + LazyProperty::Computed( + InlayTooltip::String(it) | InlayTooltip::Markdown(it), + ) => it, + LazyProperty::Lazy => "", }), ) .finish(), @@ -677,7 +681,7 @@ impl InlayHintLabelBuilder<'_> { if !text.is_empty() { self.result.parts.push(InlayHintLabelPart { text, - linked_location: self.location.take().map(Lazy::Computed), + linked_location: self.location.take().map(LazyProperty::Computed), tooltip: None, }); } @@ -797,7 +801,7 @@ fn ty_to_text_edit( ty: &hir::Type, offset_to_insert: TextSize, prefix: impl Into, -) -> Option> { +) -> Option> { // FIXME: Limit the length and bail out on excess somehow? let rendered = sema .scope(node_for_hint) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 477fb5156be6..8471547727fe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -83,7 +83,7 @@ mod tests { fixture, inlay_hints::{ tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, - Lazy, + LazyProperty, }, InlayHintsConfig, }; @@ -102,7 +102,7 @@ mod tests { let (analysis, file_id) = fixture::file(ra_fixture); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { - if let Some(Lazy::Computed(loc)) = &mut hint.linked_location { + if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location { loc.range = TextRange::empty(TextSize::from(0)); } }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 64f1f83d3f5c..bd36e2c3be6f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -12,7 +12,7 @@ use syntax::{ }; use crate::{ - inlay_hints::Lazy, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, }; pub(super) fn hints( @@ -143,7 +143,7 @@ pub(super) fn hints( acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, - label: InlayHintLabel::simple(label, None, linked_location.map(Lazy::Computed)), + label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)), text_edit: None, position: InlayHintPosition::After, pad_left: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 346e2862b0fd..6ec95ccb62a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,7 +91,7 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, - InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, + InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, LazyProperty }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 4947576f1977..2a7d95d560d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -11,8 +11,8 @@ use ide::{ Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, - InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, - NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty, + Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; use ide_db::{assists, rust_doc::format_docs, FxHasher}; @@ -549,12 +549,11 @@ pub(crate) fn inlay_hint( ) -> Cancellable { let hint_needs_resolve = |hint: &InlayHint| -> Option { hint.resolve_parent.filter(|_| { - hint.text_edit.is_some() - || hint - .label - .parts - .iter() - .any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy) + || hint.label.parts.iter().any(|part| { + part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy) + || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy) + }) }) }; @@ -569,22 +568,21 @@ pub(crate) fn inlay_hint( }); let mut something_to_resolve = false; - let text_edits = if snap - .config - .visual_studio_code_version() - .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_range_and_hash.is_some() - && fields_to_resolve.resolve_text_edits - { - something_to_resolve |= inlay_hint.text_edit.is_some(); - None - } else { - inlay_hint - .text_edit - .take() - .and_then(|it| it.computed()) - .map(|it| text_edit_vec(line_index, it)) - }; + let text_edits = inlay_hint + .text_edit + .take() + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + something_to_resolve |= + snap.config.visual_studio_code_version().is_none_or(|version| { + VersionReq::parse(">=1.86.0").unwrap().matches(version) + }) && resolve_range_and_hash.is_some() + && fields_to_resolve.resolve_text_edits; + None + } + }) + .map(|it| text_edit_vec(line_index, it)); let (label, tooltip) = inlay_hint_label( snap, fields_to_resolve, @@ -637,22 +635,23 @@ fn inlay_hint_label( let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); - let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - *something_to_resolve |= tooltip.is_some(); - None - } else { - match tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - })) - } - None => None, + let tooltip = tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= + needs_resolve && fields_to_resolve.resolve_hint_tooltip; + None } + }); + let hint_tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)), + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + })) + } + None => None, }; (lsp_types::InlayHintLabel::String(text), hint_tooltip) } @@ -661,34 +660,38 @@ fn inlay_hint_label( .parts .into_iter() .map(|part| { - let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - *something_to_resolve |= part.tooltip.is_some(); - None - } else { - match part.tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( - lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - }, - )) - } - None => None, + let tooltip = part.tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_tooltip; + None } + }); + let tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::String(s)) + } + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( + lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + }, + )) + } + None => None, }; - let location = if needs_resolve && fields_to_resolve.resolve_label_location { - *something_to_resolve |= part.linked_location.is_some(); - None - } else { - part.linked_location - .and_then(|it| it.computed()) - .map(|range| location(snap, range)) - .transpose()? - }; + let location = part + .linked_location + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_location; + None + } + }) + .map(|range| location(snap, range)) + .transpose()?; Ok(lsp_types::InlayHintLabelPart { value: part.text, tooltip, From 5059c1a870bef3b366f6863510b2bff31ba07a0a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 21 Jan 2025 18:51:09 +0200 Subject: [PATCH 093/342] Provide a config to control auto-insertion of `await` and `iter()` --- .../ide-completion/src/completions/dot.rs | 121 ++++++++++-------- .../crates/ide-completion/src/config.rs | 2 + .../crates/ide-completion/src/tests.rs | 2 + .../crates/rust-analyzer/src/config.rs | 6 + .../src/integrated_benchmarks.rs | 6 + .../docs/user/generated_config.adoc | 10 ++ .../rust-analyzer/editors/code/package.json | 20 +++ 7 files changed, 111 insertions(+), 56 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7679d9076ded..0d1765a7b76f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -42,31 +42,38 @@ pub(crate) fn complete_dot( item.detail("expr.await"); item.add_to(acc, ctx.db); - // Completions that skip `.await`, e.g. `.await.foo()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, - }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_fields( - acc, - ctx, - &future_output, - |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty), - |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), - is_field_access, - is_method_access_with_parens, - ); - complete_methods(ctx, &future_output, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) - }); + if ctx.config.enable_auto_await { + // Completions that skip `.await`, e.g. `.await.foo()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { + original: future_output.clone(), + adjusted: None, + }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_fields( + acc, + ctx, + &future_output, + |acc, field, ty| { + acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty) + }, + |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), + is_field_access, + is_method_access_with_parens, + ); + complete_methods(ctx, &future_output, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) + }); + } } complete_fields( @@ -82,39 +89,41 @@ pub(crate) fn complete_dot( acc.add_method(ctx, dot_access, func, None, None) }); - // FIXME: - // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute - // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. - // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid - let iter = receiver_ty - .strip_references() - .add_reference(hir::Mutability::Shared) - .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("iter()"))); - // Does ::IntoIter` exist? - let into_iter = || { - receiver_ty - .clone() + if ctx.config.enable_auto_iter { + // FIXME: + // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute + // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. + // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid + let iter = receiver_ty + .strip_references() + .add_reference(hir::Mutability::Shared) .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("into_iter()"))) - }; - if let Some((iter, iter_sym)) = iter.or_else(into_iter) { - // Skip iterators, e.g. complete `.iter().filter_map()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, + .map(|ty| (ty, SmolStr::new_static("iter()"))); + // Does ::IntoIter` exist? + let into_iter = || { + receiver_ty + .clone() + .into_iterator_iter(ctx.db) + .map(|ty| (ty, SmolStr::new_static("into_iter()"))) }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_methods(ctx, &iter, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) - }); + if let Some((iter, iter_sym)) = iter.or_else(into_iter) { + // Skip iterators, e.g. complete `.iter().filter_map()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_methods(ctx, &iter, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) + }); + } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 8b1ce11e8a45..c641df38ff23 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -14,6 +14,8 @@ pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, + pub enable_auto_iter: bool, + pub enable_auto_await: bool, pub enable_private_editable: bool, pub enable_term_search: bool, pub term_search_fuel: u64, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index b7dbf0a6306c..9d91f95eb65b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -87,6 +87,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 3dc4379258fa..44325fa1a29e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -453,6 +453,10 @@ config_data! { /// /// In `match` arms it completes a comma instead. completion_addSemicolonToUnit: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + completion_autoAwait_enable: bool = true, + /// Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + completion_autoIter_enable: bool = true, /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, @@ -1484,6 +1488,8 @@ impl Config { enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_auto_iter: *self.completion_autoIter_enable(source_root), + enable_auto_await: *self.completion_autoAwait_enable(source_root), enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), full_function_signatures: self .completion_fullFunctionSignatures_enable(source_root) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index fcfd06679bf2..5cdc51a1c199 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -176,6 +176,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -226,6 +228,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -274,6 +278,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index bd091db58d3f..b33de1956b8d 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -274,6 +274,16 @@ Whether to automatically add a semicolon when completing unit-returning function In `match` arms it completes a comma instead. -- +[[rust-analyzer.completion.autoAwait.enable]]rust-analyzer.completion.autoAwait.enable (default: `true`):: ++ +-- +Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. +-- +[[rust-analyzer.completion.autoIter.enable]]rust-analyzer.completion.autoIter.enable (default: `true`):: ++ +-- +Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. +-- [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: + -- diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 8b066377f2b2..f148041ac3eb 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1143,6 +1143,26 @@ } } }, + { + "title": "completion", + "properties": { + "rust-analyzer.completion.autoAwait.enable": { + "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.", + "default": true, + "type": "boolean" + } + } + }, + { + "title": "completion", + "properties": { + "rust-analyzer.completion.autoIter.enable": { + "markdownDescription": "Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.", + "default": true, + "type": "boolean" + } + } + }, { "title": "completion", "properties": { From 699296d3863d675f688cf35e9b9c67b94d034c60 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 18 Jan 2025 22:02:10 +0000 Subject: [PATCH 094/342] Move supertrait_def_ids into the elaborate module like all other fns --- clippy_lints/src/len_zero.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 5418acc105eb..26bea8d633a3 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -18,6 +18,7 @@ use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; +use rustc_trait_selection::traits::supertrait_def_ids; declare_clippy_lint! { /// ### What it does @@ -270,7 +271,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items // fill the set with current and super traits fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) { if set.insert(traitt) { - for supertrait in cx.tcx.supertrait_def_ids(traitt) { + for supertrait in supertrait_def_ids(cx.tcx, traitt) { fill_trait_set(supertrait, set, cx); } } From c5ccf86e90428ce6fb601c27c3f2938099cbd120 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 21 Jan 2025 11:06:05 -0800 Subject: [PATCH 095/342] internal: Treat cfg fetching failures as a warning If the user doesn't have rustc on $PATH, rust-analyzer won't be able to run `rustc --print cfg`. This isn't really an error, as rust-analyzer can still proceed without it. This is particularly noticeable when loading crates defined in a rust-project.json. Until the configuration is loaded, the opened files are briefly treated as detached files and users see this error. Environments with rust-project.json generally have a sysroot and rustc elsewhere, so the error confuses users. --- .../crates/project-model/src/toolchain_info/rustc_cfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs index 4bf9b59e7d03..e472da0c89b0 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -19,7 +19,7 @@ pub fn get( let rustc_cfgs = match rustc_cfgs { Ok(cfgs) => cfgs, Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); + tracing::warn!(?e, "failed to get rustc cfgs"); return vec![]; } }; From 98582b23ab4812ac57c5b989055f87e25c751ce9 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 7 Jan 2025 16:37:49 -0800 Subject: [PATCH 096/342] manual: Document all rust-project.json fields Ensure that all the fields that rust-analyzer understands are in the manual, they all have doc comments, and they use consistent punctuation (`;` rather than mixing `,` and `;`). Whilst we're here, fix the `sysroot_src` example and add 2024 as a legal value for Rust edition. --- .../crates/project-model/src/project_json.rs | 2 +- src/tools/rust-analyzer/docs/user/manual.adoc | 90 +++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 6a88cf022dfb..a39639676104 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -63,7 +63,7 @@ use crate::{ManifestPath, TargetKind}; pub struct ProjectJson { /// e.g. `path/to/sysroot` pub(crate) sysroot: Option, - /// e.g. `path/to/sysroot/lib/rustlib/src/rust` + /// e.g. `path/to/sysroot/lib/rustlib/src/rust/library` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, /// The path to the rust-project.json file. May be None if this diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index ffc820e9b7f7..4a2a6f2e3686 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -716,6 +716,32 @@ interface JsonProject { /// dependencies as well as sysroot crate (libstd, /// libcore and such). crates: Crate[]; + /// Configuration for CLI commands. + /// + /// These are used for running and debugging binaries + /// and tests without encoding build system-specific + /// knowledge into rust-analyzer. + /// + /// # Example + /// + /// Below is an example of a test runnable. `{label}` and `{test_id}` + /// are explained in `Runnable::args`'s documentation below. + /// + /// ```json + /// { + /// "program": "buck", + /// "args": [ + /// "test", + /// "{label}", + /// "--", + /// "{test_id}", + /// "--print-passing-details" + /// ], + /// "cwd": "/home/user/repo-root/", + /// "kind": "testOne" + /// } + /// ``` + runnables?: Runnable[]; } interface Crate { @@ -726,7 +752,10 @@ interface Crate { /// Path to the root module of the crate. root_module: string; /// Edition of the crate. - edition: "2015" | "2018" | "2021"; + edition: '2015' | '2018' | '2021' | '2024'; + /// The version of the crate. Used for calculating + /// the correct docs.rs URL. + version?: string; /// Dependencies deps: Dep[]; /// Should this crate be treated as a member of @@ -757,9 +786,9 @@ interface Crate { /// rust-analyzer assumes that files from one /// source can't refer to files in another source. source?: { - include_dirs: string[], - exclude_dirs: string[], - }, + include_dirs: string[]; + exclude_dirs: string[]; + }; /// List of cfg groups this crate inherits. /// /// All cfg in these groups will be concatenated to @@ -776,21 +805,68 @@ interface Crate { target?: string; /// Environment variables, used for /// the `env!` macro - env: { [key: string]: string; }, + env: { [key: string]: string; }; /// Whether the crate is a proc-macro crate. is_proc_macro: boolean; /// For proc-macro crates, path to compiled /// proc-macro (.so file). proc_macro_dylib_path?: string; + + /// Repository, matching the URL that would be used + /// in Cargo.toml. + repository?: string; + + /// Build-specific data about this crate. + build?: BuildInfo; } interface Dep { /// Index of a crate in the `crates` array. - crate: number, + crate: number; /// Name as should appear in the (implicit) /// `extern crate name` declaration. - name: string, + name: string; +} + +interface BuildInfo { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to `Crate::display_name`. + label: string; + /// Path corresponding to the build system-specific file defining the crate. + build_file: string; + /// The kind of target. + /// + /// This information is used to determine what sort + /// of runnable codelens to provide, if any. + target_kind: 'bin' | 'lib' | 'test'; +} + +interface Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + program: string; + /// The arguments passed to `program`. + args: string[]; + /// The current working directory of the runnable. + cwd: string; + /// Used to decide what code lens to offer. + /// + /// `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// + /// The args for testOne can contain two template strings: + /// `{label}` and `{test_id}`. `{label}` will be replaced + /// with the `Build::label` and `{test_id}` will be replaced + /// with the test name. + kind: 'testOne' | string; } ---- From 7d9fe91ccbf350613dd20a39133ad9984999a20b Mon Sep 17 00:00:00 2001 From: Caio Date: Tue, 21 Jan 2025 17:54:16 -0300 Subject: [PATCH 097/342] [cfg_match] Document the use of expressions --- library/core/src/macros/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5c04e5a40df0..01a3c9d2ada7 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -313,6 +313,17 @@ pub macro cfg_match { /// } /// } /// ``` +/// +/// If desired, it is possible to return expressions through the use of surrounding braces: +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// let _some_string = cfg_match! {{ +/// unix => { "With great power comes great electricity bills" } +/// _ => { "Behind every successful diet is an unwatched pizza" } +/// }}; +/// ``` #[cfg(not(bootstrap))] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] From 6b48b67dfcc1f75bbf19ec690d498129e19360ab Mon Sep 17 00:00:00 2001 From: edwloef Date: Tue, 21 Jan 2025 22:28:04 +0100 Subject: [PATCH 098/342] optimize slice::ptr_rotate for compile-time-constant small rotates --- library/core/src/slice/rotate.rs | 331 ++++++++++++++++--------------- 1 file changed, 168 insertions(+), 163 deletions(-) diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index d8e0acb565c8..20833dc31aa2 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -11,11 +11,18 @@ use crate::{cmp, ptr}; /// /// # Algorithm /// -/// Algorithm 1 is used for small values of `left + right` or for large `T`. The elements are moved -/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps -/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at -/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over -/// elements. For example: +/// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The +/// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and +/// the ones on the buffer are moved back into the hole on the opposite side of where they +/// originated. +/// +/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough. +/// +/// Algorithm 2 is otherwise used for small values of `left + right` or for large `T`. The elements +/// are moved into their final positions one at a time starting at `mid - left` and advancing by +/// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we +/// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps +/// skipped over elements. For example: /// ```text /// left = 10, right = 6 /// the `^` indicates an element in its final place @@ -39,13 +46,7 @@ use crate::{cmp, ptr}; /// `gcd(left + right, right)` value). The end result is that all elements are finalized once and /// only once. /// -/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to -/// fit onto a stack buffer. The `min(left, right)` elements are copied onto the buffer, `memmove` -/// is applied to the others, and the ones on the buffer are moved back into the hole on the -/// opposite side of where they originated. -/// -/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough. -/// Algorithm 1 can be vectorized by chunking and performing many rounds at once, but there are too +/// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too /// few rounds on average until `left + right` is enormous, and the worst case of a single /// round is always there. Instead, algorithm 3 utilizes repeated swapping of /// `min(left, right)` elements until a smaller rotate problem is left. @@ -65,172 +66,176 @@ pub(super) unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: if T::IS_ZST { return; } - loop { - // N.B. the below algorithms can fail if these cases are not checked - if (right == 0) || (left == 0) { - return; + // N.B. the below algorithms can fail if these cases are not checked + if (right == 0) || (left == 0) { + return; + } + // `T` is not a zero-sized type, so it's okay to divide by its size. + if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + { + // Algorithm 1 + // The `[T; 0]` here is to ensure this is appropriately aligned for T + let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); + let buf = rawarray.as_mut_ptr() as *mut T; + // SAFETY: `mid-left <= mid-left+right < mid+right` + let dim = unsafe { mid.sub(left).add(right) }; + if left <= right { + // SAFETY: + // + // 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in + // `buf` without overflow and `buf` was created just above and so cannot be + // overlapped with any value of `[mid-left; left]` + // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care + // about overlaps here. + // 3) The `if` condition about `left <= right` ensures writing `left` elements to + // `dim = mid-left+right` is valid because: + // - `buf` is valid and `left` elements were written in it in 1) + // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` + unsafe { + // 1) + ptr::copy_nonoverlapping(mid.sub(left), buf, left); + // 2) + ptr::copy(mid, mid.sub(left), right); + // 3) + ptr::copy_nonoverlapping(buf, dim, left); + } + } else { + // SAFETY: same reasoning as above but with `left` and `right` reversed + unsafe { + ptr::copy_nonoverlapping(mid, buf, right); + ptr::copy(mid.sub(left), dim, left); + ptr::copy_nonoverlapping(buf, mid.sub(left), right); + } } - if !cfg!(feature = "optimize_for_size") - && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) - { - // Algorithm 1 - // Microbenchmarks indicate that the average performance for random shifts is better all - // the way until about `left + right == 32`, but the worst case performance breaks even - // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 - // `usize`s, this algorithm also outperforms other algorithms. - // SAFETY: callers must ensure `mid - left` is valid for reading and writing. - let x = unsafe { mid.sub(left) }; - // beginning of first round - // SAFETY: see previous comment. - let mut tmp: T = unsafe { x.read() }; - let mut i = right; - // `gcd` can be found before hand by calculating `gcd(left + right, right)`, - // but it is faster to do one loop which calculates the gcd as a side effect, then - // doing the rest of the chunk - let mut gcd = right; - // benchmarks reveal that it is faster to swap temporaries all the way through instead - // of reading one temporary once, copying backwards, and then writing that temporary at - // the very end. This is possibly due to the fact that swapping or replacing temporaries - // uses only one memory address in the loop instead of needing to manage two. + } else if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + { + // Algorithm 2 + // Microbenchmarks indicate that the average performance for random shifts is better all + // the way until about `left + right == 32`, but the worst case performance breaks even + // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 + // `usize`s, this algorithm also outperforms other algorithms. + // SAFETY: callers must ensure `mid - left` is valid for reading and writing. + let x = unsafe { mid.sub(left) }; + // beginning of first round + // SAFETY: see previous comment. + let mut tmp: T = unsafe { x.read() }; + let mut i = right; + // `gcd` can be found before hand by calculating `gcd(left + right, right)`, + // but it is faster to do one loop which calculates the gcd as a side effect, then + // doing the rest of the chunk + let mut gcd = right; + // benchmarks reveal that it is faster to swap temporaries all the way through instead + // of reading one temporary once, copying backwards, and then writing that temporary at + // the very end. This is possibly due to the fact that swapping or replacing temporaries + // uses only one memory address in the loop instead of needing to manage two. + loop { + // [long-safety-expl] + // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and + // writing. + // + // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` + // - `i <= left+right-1` is always true + // - if `i < left`, `right` is added so `i < left+right` and on the next + // iteration `left` is removed from `i` so it doesn't go further + // - if `i >= left`, `left` is removed immediately and so it doesn't go further. + // - overflows cannot happen for `i` since the function's safety contract ask for + // `mid+right-1 = x+left+right` to be valid for writing + // - underflows cannot happen because `i` must be bigger or equal to `left` for + // a subtraction of `left` to happen. + // + // So `x+i` is valid for reading and writing if the caller respected the contract + tmp = unsafe { x.add(i).replace(tmp) }; + // instead of incrementing `i` and then checking if it is outside the bounds, we + // check if `i` will go outside the bounds on the next increment. This prevents + // any wrapping of pointers or `usize`. + if i >= left { + i -= left; + if i == 0 { + // end of first round + // SAFETY: tmp has been read from a valid source and x is valid for writing + // according to the caller. + unsafe { x.write(tmp) }; + break; + } + // this conditional must be here if `left + right >= 15` + if i < gcd { + gcd = i; + } + } else { + i += right; + } + } + // finish the chunk with more rounds + for start in 1..gcd { + // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for + // reading and writing as per the function's safety contract, see [long-safety-expl] + // above + tmp = unsafe { x.add(start).read() }; + // [safety-expl-addition] + // + // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the + // greatest common divisor of `(left+right, right)` means that `left = right` so + // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing + // according to the function's safety contract. + i = start + right; loop { - // [long-safety-expl] - // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and - // writing. - // - // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` - // - `i <= left+right-1` is always true - // - if `i < left`, `right` is added so `i < left+right` and on the next - // iteration `left` is removed from `i` so it doesn't go further - // - if `i >= left`, `left` is removed immediately and so it doesn't go further. - // - overflows cannot happen for `i` since the function's safety contract ask for - // `mid+right-1 = x+left+right` to be valid for writing - // - underflows cannot happen because `i` must be bigger or equal to `left` for - // a subtraction of `left` to happen. - // - // So `x+i` is valid for reading and writing if the caller respected the contract + // SAFETY: see [long-safety-expl] and [safety-expl-addition] tmp = unsafe { x.add(i).replace(tmp) }; - // instead of incrementing `i` and then checking if it is outside the bounds, we - // check if `i` will go outside the bounds on the next increment. This prevents - // any wrapping of pointers or `usize`. if i >= left { i -= left; - if i == 0 { - // end of first round - // SAFETY: tmp has been read from a valid source and x is valid for writing - // according to the caller. - unsafe { x.write(tmp) }; + if i == start { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + unsafe { x.add(start).write(tmp) }; break; } - // this conditional must be here if `left + right >= 15` - if i < gcd { - gcd = i; - } } else { i += right; } } - // finish the chunk with more rounds - for start in 1..gcd { - // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for - // reading and writing as per the function's safety contract, see [long-safety-expl] - // above - tmp = unsafe { x.add(start).read() }; - // [safety-expl-addition] - // - // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the - // greatest common divisor of `(left+right, right)` means that `left = right` so - // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing - // according to the function's safety contract. - i = start + right; + } + } else { + loop { + if left >= right { + // Algorithm 3 + // There is an alternate way of swapping that involves finding where the last swap + // of this algorithm would be, and swapping using that last chunk instead of swapping + // adjacent chunks like this algorithm is doing, but this way is still faster. loop { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - tmp = unsafe { x.add(i).replace(tmp) }; - if i >= left { - i -= left; - if i == start { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - unsafe { x.add(start).write(tmp) }; - break; - } - } else { - i += right; + // SAFETY: + // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing + // Subtracting `right` from `mid` each turn is counterbalanced by the addition and + // check after it. + unsafe { + ptr::swap_nonoverlapping(mid.sub(right), mid, right); + mid = mid.sub(right); + } + left -= right; + if left < right { + break; + } + } + } else { + // Algorithm 3, `left < right` + loop { + // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because + // `left < right` so `mid+left < mid+right`. + // Adding `left` to `mid` each turn is counterbalanced by the subtraction and check + // after it. + unsafe { + ptr::swap_nonoverlapping(mid.sub(left), mid, left); + mid = mid.add(left); + } + right -= left; + if right < left { + break; } } } - return; - // `T` is not a zero-sized type, so it's okay to divide by its size. - } else if !cfg!(feature = "optimize_for_size") - && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() - { - // Algorithm 2 - // The `[T; 0]` here is to ensure this is appropriately aligned for T - let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); - let buf = rawarray.as_mut_ptr() as *mut T; - // SAFETY: `mid-left <= mid-left+right < mid+right` - let dim = unsafe { mid.sub(left).add(right) }; - if left <= right { - // SAFETY: - // - // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in - // `buf` without overflow and `buf` was created just above and so cannot be - // overlapped with any value of `[mid-left; left]` - // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care - // about overlaps here. - // 3) The `if` condition about `left <= right` ensures writing `left` elements to - // `dim = mid-left+right` is valid because: - // - `buf` is valid and `left` elements were written in it in 1) - // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` - unsafe { - // 1) - ptr::copy_nonoverlapping(mid.sub(left), buf, left); - // 2) - ptr::copy(mid, mid.sub(left), right); - // 3) - ptr::copy_nonoverlapping(buf, dim, left); - } - } else { - // SAFETY: same reasoning as above but with `left` and `right` reversed - unsafe { - ptr::copy_nonoverlapping(mid, buf, right); - ptr::copy(mid.sub(left), dim, left); - ptr::copy_nonoverlapping(buf, mid.sub(left), right); - } - } - return; - } else if left >= right { - // Algorithm 3 - // There is an alternate way of swapping that involves finding where the last swap - // of this algorithm would be, and swapping using that last chunk instead of swapping - // adjacent chunks like this algorithm is doing, but this way is still faster. - loop { - // SAFETY: - // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing - // Subtracting `right` from `mid` each turn is counterbalanced by the addition and - // check after it. - unsafe { - ptr::swap_nonoverlapping(mid.sub(right), mid, right); - mid = mid.sub(right); - } - left -= right; - if left < right { - break; - } - } - } else { - // Algorithm 3, `left < right` - loop { - // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because - // `left < right` so `mid+left < mid+right`. - // Adding `left` to `mid` each turn is counterbalanced by the subtraction and check - // after it. - unsafe { - ptr::swap_nonoverlapping(mid.sub(left), mid, left); - mid = mid.add(left); - } - right -= left; - if right < left { - break; - } + + if (right == 0) || (left == 0) { + return; } } } From cc19dfa125e280545e6e599f1aaf27ffb0c55ffd Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Wed, 22 Jan 2025 00:20:54 +0100 Subject: [PATCH 099/342] Add `AsyncFn*` to core prelude --- library/core/src/prelude/common.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs index e38ef1e147c7..8b116cecb529 100644 --- a/library/core/src/prelude/common.rs +++ b/library/core/src/prelude/common.rs @@ -12,6 +12,9 @@ pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; +#[stable(feature = "async_closure", since = "1.85.0")] +#[doc(no_inline)] +pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; // Re-exported functions #[stable(feature = "core_prelude", since = "1.4.0")] From a18b75a87ed29a074cb751c840ccff65cbd12815 Mon Sep 17 00:00:00 2001 From: Joshua Wong Date: Tue, 21 Jan 2025 19:50:48 -0500 Subject: [PATCH 100/342] docs: fix verbose-bit-mask example changelog: none `x & 15 == 0` is not equivalent to `x.trailing_zeros() > 4`, as `x = 0b10000` is true for the former and false for the latter. In fact, clippy itself suggests the following: ```rust pub fn src(x: i32) -> bool { x & 15 == 0 // ~error: bit mask could be simplified with a call to `trailing_zeros` ^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4` } ``` --- clippy_lints/src/operators/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 9e8a821c3f4e..d9845bc3b0f7 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -262,7 +262,7 @@ declare_clippy_lint! { /// to `trailing_zeros` /// /// ### Why is this bad? - /// `x.trailing_zeros() > 4` is much clearer than `x & 15 + /// `x.trailing_zeros() >= 4` is much clearer than `x & 15 /// == 0` /// /// ### Known problems @@ -278,7 +278,7 @@ declare_clippy_lint! { /// /// ```no_run /// # let x: i32 = 1; - /// if x.trailing_zeros() > 4 { } + /// if x.trailing_zeros() >= 4 { } /// ``` #[clippy::version = "pre 1.29.0"] pub VERBOSE_BIT_MASK, From 26838f8552bcb6bf38a10b56d0548580bf82c706 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Wed, 22 Jan 2025 04:55:42 +0900 Subject: [PATCH 101/342] don't trigger `needless_late_init` when the first usage is in macro --- clippy_lints/src/needless_late_init.rs | 4 ++++ tests/ui/needless_late_init.fixed | 11 +++++++++++ tests/ui/needless_late_init.rs | 11 +++++++++++ 3 files changed, 26 insertions(+) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a67addea9486..dca3c0b10cdb 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -107,6 +107,10 @@ struct LocalAssign { impl LocalAssign { fn from_expr(expr: &Expr<'_>, span: Span) -> Option { + if expr.span.from_expansion() { + return None; + } + if let ExprKind::Assign(lhs, rhs, _) = expr.kind { if lhs.span.from_expansion() { return None; diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index 6db870490445..b4bd53ce7bf7 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -270,3 +270,14 @@ fn issue8911() -> u32 { 3 } + +macro_rules! issue13776_mac { + ($var:expr, $val:literal) => { + $var = $val; + }; +} + +fn issue13776() { + let x; + issue13776_mac!(x, 10); // should not lint +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index c1e86212a08b..e25483625a68 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -270,3 +270,14 @@ fn issue8911() -> u32 { 3 } + +macro_rules! issue13776_mac { + ($var:expr, $val:literal) => { + $var = $val; + }; +} + +fn issue13776() { + let x; + issue13776_mac!(x, 10); // should not lint +} From 1fe351baae7170f982a837e1a08b57975f615add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 21 Jan 2025 17:22:18 +0100 Subject: [PATCH 102/342] Apply LTO config to rustdoc Before, the LTO configuration from `config.toml` was not applied to `rustdoc`. --- src/bootstrap/src/core/build_steps/tool.rs | 19 ++++++++++++++++--- src/bootstrap/src/core/builder/cargo.rs | 12 +++++++----- src/bootstrap/src/core/builder/mod.rs | 2 +- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 9ae03ac7fe07..a0abd439de02 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -4,8 +4,10 @@ use std::{env, fs}; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; use crate::core::builder; -use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; -use crate::core::config::{DebuginfoLevel, TargetSelection}; +use crate::core::builder::{ + Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step, cargo_profile_var, +}; +use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{add_dylib_path, exe, t}; @@ -645,7 +647,7 @@ impl Step for Rustdoc { } // NOTE: Never modify the rustflags here, it breaks the build cache for other tools! - let cargo = prepare_tool_cargo( + let mut cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -656,6 +658,17 @@ impl Step for Rustdoc { features.as_slice(), ); + // rustdoc is performance sensitive, so apply LTO to it. + let lto = match builder.config.rust_lto { + RustcLto::Off => Some("off"), + RustcLto::Thin => Some("thin"), + RustcLto::Fat => Some("fat"), + RustcLto::ThinLocal => None, + }; + if let Some(lto) = lto { + cargo.env(cargo_profile_var("LTO", &builder.config), lto); + } + let _guard = builder.msg_tool( Kind::Build, Mode::ToolRustc, diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index f9fb19ddb095..9ef48449072d 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -10,7 +10,7 @@ use crate::core::config::flags::Color; use crate::utils::build_stamp; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; use crate::{ - BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, + BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, TargetSelection, command, prepare_behaviour_dump_dir, t, }; @@ -473,10 +473,7 @@ impl Builder<'_> { build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } - let profile_var = |name: &str| { - let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) - }; + let profile_var = |name: &str| cargo_profile_var(name, &self.config); // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config // needs to not accidentally link to libLLVM in stage0/lib. @@ -1221,3 +1218,8 @@ impl Builder<'_> { } } } + +pub fn cargo_profile_var(name: &str, config: &Config) -> String { + let profile = if config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; + format!("CARGO_PROFILE_{}_{}", profile, name) +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index b293ac4f3515..04a00fde3ab4 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -11,7 +11,7 @@ use std::{env, fs}; use clap::ValueEnum; -pub use self::cargo::Cargo; +pub use self::cargo::{Cargo, cargo_profile_var}; pub use crate::Compiler; use crate::core::build_steps::{ check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index a3eb781f1476..6f62df28e494 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -340,4 +340,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Change the compiler profile to default to rust.debug-assertions = true", }, + ChangeInfo { + change_id: 135832, + severity: ChangeSeverity::Info, + summary: "Rustdoc now respects the value of rust.lto.", + }, ]; From 71ba2cf1e5e2eb75824b993cf389f27701f2dd2a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 22 Jan 2025 13:03:30 +0100 Subject: [PATCH 103/342] Extract `leaks_droppable_temporary_with_limited_lifetime()` --- clippy_lints/src/returns.rs | 25 ++++++------------------- clippy_utils/src/lib.rs | 23 ++++++++++++++++++++--- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index dfaee8cc3054..664e984fece3 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{Descend, for_each_expr, for_each_unconsumed_temporary}; +use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{ - binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, - path_to_local_id, span_contains_cfg, span_find_starting_semi, + binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, + leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, + span_find_starting_semi, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -389,22 +390,8 @@ fn check_final_expr<'tcx>( } }; - if let Some(inner) = inner { - if for_each_unconsumed_temporary(cx, inner, |temporary_ty| { - if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) - && temporary_ty - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_break() - { - return; - } + if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { + return; } if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8b39e2aa4e25..6869078ba0ad 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -117,15 +117,15 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, Ty, TyCtxt, - TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, + TyCtxt, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, sym}; use rustc_target::abi::Integer; -use visitors::Visitable; +use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; @@ -3465,3 +3465,20 @@ pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } false } + +/// Returns true if `expr` creates any temporary whose type references a non-static lifetime and has +/// a significant drop and does not consume it. +pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_unconsumed_temporary(cx, expr, |temporary_ty| { + if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) + && temporary_ty + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_break() +} From 9dca770aec2ef3cec721e5e9852d8a2c646695f0 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 20 Jan 2025 23:22:20 +0100 Subject: [PATCH 104/342] `unnecessary_semicolon`: do not lint if it may cause borrow errors Before edition 2024, some temporaries used in scrutinees in a `match` used as the last expression of a block may outlive some referenced local variables. Prevent those cases from happening by checking that alive temporaries with significant drop do have a static lifetime. The check is performed only for edition 2021 and earlier, and for the last statement if it would become the last expression of the block. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unnecessary_semicolon.rs | 56 ++++++++++++++++-- .../unnecessary_semicolon.edition2021.fixed | 57 +++++++++++++++++++ .../unnecessary_semicolon.edition2021.stderr | 23 ++++++++ .../unnecessary_semicolon.edition2024.fixed | 57 +++++++++++++++++++ .../unnecessary_semicolon.edition2024.stderr | 29 ++++++++++ tests/ui/unnecessary_semicolon.rs | 25 ++++++++ 7 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 tests/ui/unnecessary_semicolon.edition2021.fixed create mode 100644 tests/ui/unnecessary_semicolon.edition2021.stderr create mode 100644 tests/ui/unnecessary_semicolon.edition2024.fixed create mode 100644 tests/ui/unnecessary_semicolon.edition2024.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7888119567b9..85f99a9e0559 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -973,6 +973,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); - store.register_late_pass(|_| Box::new(unnecessary_semicolon::UnnecessarySemicolon)); + store.register_late_pass(|_| Box::::default()); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_semicolon.rs b/clippy_lints/src/unnecessary_semicolon.rs index 6bc56dffc57a..efbc536dcb4d 100644 --- a/clippy_lints/src/unnecessary_semicolon.rs +++ b/clippy_lints/src/unnecessary_semicolon.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::leaks_droppable_temporary_with_limited_lifetime; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, MatchSource, Stmt, StmtKind}; +use rustc_hir::{Block, ExprKind, HirId, MatchSource, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; +use rustc_span::edition::Edition::Edition2021; declare_clippy_lint! { /// ### What it does @@ -33,10 +35,50 @@ declare_clippy_lint! { "unnecessary semicolon after expression returning `()`" } -declare_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); +#[derive(Default)] +pub struct UnnecessarySemicolon { + last_statements: Vec, +} -impl LateLintPass<'_> for UnnecessarySemicolon { - fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { +impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); + +impl UnnecessarySemicolon { + /// Enter or leave a block, remembering the last statement of the block. + fn handle_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>, enter: bool) { + // Up to edition 2021, removing the semicolon of the last statement of a block + // may result in the scrutinee temporary values to live longer than the block + // variables. To avoid this problem, we do not lint the last statement of an + // expressionless block. + if cx.tcx.sess.edition() <= Edition2021 + && block.expr.is_none() + && let Some(last_stmt) = block.stmts.last() + { + if enter { + self.last_statements.push(last_stmt.hir_id); + } else { + self.last_statements.pop(); + } + } + } + + /// Checks if `stmt` is the last statement in an expressionless block for edition ≤ 2021. + fn is_last_in_block(&self, stmt: &Stmt<'_>) -> bool { + self.last_statements + .last() + .is_some_and(|last_stmt_id| last_stmt_id == &stmt.hir_id) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { + fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + self.handle_block(cx, block, true); + } + + fn check_block_post(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + self.handle_block(cx, block, false); + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) { // rustfmt already takes care of removing semicolons at the end // of loops. if let StmtKind::Semi(expr) = stmt.kind @@ -48,6 +90,10 @@ impl LateLintPass<'_> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit { + if self.is_last_in_block(stmt) && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { + return; + } + let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi()); span_lint_and_sugg( cx, diff --git a/tests/ui/unnecessary_semicolon.edition2021.fixed b/tests/ui/unnecessary_semicolon.edition2021.fixed new file mode 100644 index 000000000000..7a3b79553dea --- /dev/null +++ b/tests/ui/unnecessary_semicolon.edition2021.fixed @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} diff --git a/tests/ui/unnecessary_semicolon.edition2021.stderr b/tests/ui/unnecessary_semicolon.edition2021.stderr new file mode 100644 index 000000000000..ccff33084172 --- /dev/null +++ b/tests/ui/unnecessary_semicolon.edition2021.stderr @@ -0,0 +1,23 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:29:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:35:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:56:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 3 previous errors + diff --git a/tests/ui/unnecessary_semicolon.edition2024.fixed b/tests/ui/unnecessary_semicolon.edition2024.fixed new file mode 100644 index 000000000000..d186d5e7ebc4 --- /dev/null +++ b/tests/ui/unnecessary_semicolon.edition2024.fixed @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} diff --git a/tests/ui/unnecessary_semicolon.edition2024.stderr b/tests/ui/unnecessary_semicolon.edition2024.stderr new file mode 100644 index 000000000000..4e526af2147d --- /dev/null +++ b/tests/ui/unnecessary_semicolon.edition2024.stderr @@ -0,0 +1,29 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:29:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:35:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:47:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:56:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unnecessary_semicolon.rs b/tests/ui/unnecessary_semicolon.rs index b6fa4f1c9cec..3028c5b27b34 100644 --- a/tests/ui/unnecessary_semicolon.rs +++ b/tests/ui/unnecessary_semicolon.rs @@ -1,5 +1,10 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + #![warn(clippy::unnecessary_semicolon)] #![feature(postfix_match)] +#![allow(clippy::single_match)] fn no_lint(mut x: u32) -> Option { Some(())?; @@ -30,3 +35,23 @@ fn main() { }; //~^ ERROR: unnecessary semicolon } + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} From 69ff46ec9c362b923957a7a59657d0c9fa1f60c3 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 22 Jan 2025 19:02:08 +0100 Subject: [PATCH 105/342] `arithmetic_side_effects`: check adjusted expression types --- .../src/operators/arithmetic_side_effects.rs | 6 +- tests/ui/arithmetic_side_effects.rs | 11 + tests/ui/arithmetic_side_effects.stderr | 274 ++++++++++-------- 3 files changed, 166 insertions(+), 125 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 03f732b2516d..9d07a14718da 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -220,7 +220,7 @@ impl ArithmeticSideEffects { actual_lhs = expr_or_init(cx, actual_lhs); actual_rhs = expr_or_init(cx, actual_rhs); let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); - let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs(); + let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs(); if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { return; } - let instance_ty = cx.typeck_results().expr_ty(receiver); + let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); if !Self::is_integral(instance_ty) { return; } @@ -311,7 +311,7 @@ impl ArithmeticSideEffects { if ConstEvalCtxt::new(cx).eval(un_expr).is_some() { return; } - let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); if self.has_allowed_unary(ty) { return; } diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index 3f2040730851..f09106773c7e 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -170,6 +170,7 @@ pub fn hard_coded_allowed() { let _ = Saturating(0u32) + Saturating(0u32); let _ = String::new() + ""; + let _ = String::new() + &String::new(); let _ = Wrapping(0u32) + Wrapping(0u32); let saturating: Saturating = Saturating(0u32); @@ -408,11 +409,14 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _n.wrapping_rem(_n); _n.wrapping_rem_euclid(_n); + _n.saturating_div(*Box::new(_n)); + // Unary _n = -_n; _n = -&_n; _custom = -_custom; _custom = -&_custom; + _ = -*Box::new(_n); } // Copied and pasted from the `integer_arithmetic` lint for comparison. @@ -534,4 +538,11 @@ pub fn issue_12318() { one.sub_assign(1); } +pub fn explicit_methods() { + use core::ops::Add; + let one: i32 = 1; + one.add(&one); + Box::new(one).add(one); +} + fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 78b1aca4b8a4..9b4cfb83fbb2 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -14,730 +14,760 @@ LL | let _ = 1f128 + 1f128; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:307:5 + --> tests/ui/arithmetic_side_effects.rs:173:13 + | +LL | let _ = String::new() + &String::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:308:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:308:5 + --> tests/ui/arithmetic_side_effects.rs:309:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:309:5 + --> tests/ui/arithmetic_side_effects.rs:310:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:310:5 + --> tests/ui/arithmetic_side_effects.rs:311:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:311:5 + --> tests/ui/arithmetic_side_effects.rs:312:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:312:5 + --> tests/ui/arithmetic_side_effects.rs:313:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:313:5 + --> tests/ui/arithmetic_side_effects.rs:314:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:314:5 + --> tests/ui/arithmetic_side_effects.rs:315:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:315:5 + --> tests/ui/arithmetic_side_effects.rs:316:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:316:5 + --> tests/ui/arithmetic_side_effects.rs:317:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:317:5 + --> tests/ui/arithmetic_side_effects.rs:318:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:318:5 + --> tests/ui/arithmetic_side_effects.rs:319:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:319:5 + --> tests/ui/arithmetic_side_effects.rs:320:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:320:5 + --> tests/ui/arithmetic_side_effects.rs:321:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:321:5 + --> tests/ui/arithmetic_side_effects.rs:322:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:322:5 + --> tests/ui/arithmetic_side_effects.rs:323:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:323:5 + --> tests/ui/arithmetic_side_effects.rs:324:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:324:5 + --> tests/ui/arithmetic_side_effects.rs:325:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:325:5 + --> tests/ui/arithmetic_side_effects.rs:326:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:326:5 + --> tests/ui/arithmetic_side_effects.rs:327:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:327:5 + --> tests/ui/arithmetic_side_effects.rs:328:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:328:5 + --> tests/ui/arithmetic_side_effects.rs:329:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:329:5 + --> tests/ui/arithmetic_side_effects.rs:330:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:330:5 + --> tests/ui/arithmetic_side_effects.rs:331:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:331:5 + --> tests/ui/arithmetic_side_effects.rs:332:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:332:5 + --> tests/ui/arithmetic_side_effects.rs:333:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:333:5 + --> tests/ui/arithmetic_side_effects.rs:334:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:334:5 + --> tests/ui/arithmetic_side_effects.rs:335:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:335:5 + --> tests/ui/arithmetic_side_effects.rs:336:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:336:5 + --> tests/ui/arithmetic_side_effects.rs:337:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:337:5 + --> tests/ui/arithmetic_side_effects.rs:338:5 | LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:338:5 + --> tests/ui/arithmetic_side_effects.rs:339:5 | LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:339:5 + --> tests/ui/arithmetic_side_effects.rs:340:5 | LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:340:5 + --> tests/ui/arithmetic_side_effects.rs:341:5 | LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:341:5 + --> tests/ui/arithmetic_side_effects.rs:342:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:342:5 + --> tests/ui/arithmetic_side_effects.rs:343:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:343:5 + --> tests/ui/arithmetic_side_effects.rs:344:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:344:5 + --> tests/ui/arithmetic_side_effects.rs:345:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:345:5 + --> tests/ui/arithmetic_side_effects.rs:346:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:346:5 + --> tests/ui/arithmetic_side_effects.rs:347:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:347:5 + --> tests/ui/arithmetic_side_effects.rs:348:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:348:5 + --> tests/ui/arithmetic_side_effects.rs:349:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:350:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:350:5 + --> tests/ui/arithmetic_side_effects.rs:351:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:352:5 | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:352:5 + --> tests/ui/arithmetic_side_effects.rs:353:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:353:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:354:5 + --> tests/ui/arithmetic_side_effects.rs:355:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:357:10 + --> tests/ui/arithmetic_side_effects.rs:358:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:358:10 + --> tests/ui/arithmetic_side_effects.rs:359:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:10 + --> tests/ui/arithmetic_side_effects.rs:360:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:360:10 + --> tests/ui/arithmetic_side_effects.rs:361:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:10 + --> tests/ui/arithmetic_side_effects.rs:362:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:362:10 + --> tests/ui/arithmetic_side_effects.rs:363:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:10 + --> tests/ui/arithmetic_side_effects.rs:364:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:364:10 + --> tests/ui/arithmetic_side_effects.rs:365:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:10 + --> tests/ui/arithmetic_side_effects.rs:366:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:366:10 + --> tests/ui/arithmetic_side_effects.rs:367:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:10 + --> tests/ui/arithmetic_side_effects.rs:368:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:368:10 + --> tests/ui/arithmetic_side_effects.rs:369:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:10 + --> tests/ui/arithmetic_side_effects.rs:370:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:370:10 + --> tests/ui/arithmetic_side_effects.rs:371:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:10 + --> tests/ui/arithmetic_side_effects.rs:372:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:372:10 + --> tests/ui/arithmetic_side_effects.rs:373:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:10 + --> tests/ui/arithmetic_side_effects.rs:374:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:374:10 + --> tests/ui/arithmetic_side_effects.rs:375:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:10 + --> tests/ui/arithmetic_side_effects.rs:376:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:376:15 + --> tests/ui/arithmetic_side_effects.rs:377:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:15 + --> tests/ui/arithmetic_side_effects.rs:378:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:378:15 + --> tests/ui/arithmetic_side_effects.rs:379:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:15 + --> tests/ui/arithmetic_side_effects.rs:380:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:380:15 + --> tests/ui/arithmetic_side_effects.rs:381:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:15 + --> tests/ui/arithmetic_side_effects.rs:382:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:382:15 + --> tests/ui/arithmetic_side_effects.rs:383:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:15 + --> tests/ui/arithmetic_side_effects.rs:384:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:384:15 + --> tests/ui/arithmetic_side_effects.rs:385:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:15 + --> tests/ui/arithmetic_side_effects.rs:386:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:386:15 + --> tests/ui/arithmetic_side_effects.rs:387:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:15 + --> tests/ui/arithmetic_side_effects.rs:388:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:388:15 + --> tests/ui/arithmetic_side_effects.rs:389:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:15 + --> tests/ui/arithmetic_side_effects.rs:390:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:390:15 + --> tests/ui/arithmetic_side_effects.rs:391:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:15 + --> tests/ui/arithmetic_side_effects.rs:392:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:392:15 + --> tests/ui/arithmetic_side_effects.rs:393:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:15 + --> tests/ui/arithmetic_side_effects.rs:394:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:394:15 + --> tests/ui/arithmetic_side_effects.rs:395:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:15 + --> tests/ui/arithmetic_side_effects.rs:396:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:396:15 + --> tests/ui/arithmetic_side_effects.rs:397:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:397:15 + --> tests/ui/arithmetic_side_effects.rs:398:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:398:15 + --> tests/ui/arithmetic_side_effects.rs:399:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:401:23 + --> tests/ui/arithmetic_side_effects.rs:402:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:402:21 + --> tests/ui/arithmetic_side_effects.rs:403:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:21 + --> tests/ui/arithmetic_side_effects.rs:404:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:404:28 + --> tests/ui/arithmetic_side_effects.rs:405:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:406:23 + --> tests/ui/arithmetic_side_effects.rs:407:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:407:21 + --> tests/ui/arithmetic_side_effects.rs:408:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:408:21 + --> tests/ui/arithmetic_side_effects.rs:409:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:28 + --> tests/ui/arithmetic_side_effects.rs:410:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:412:10 + --> tests/ui/arithmetic_side_effects.rs:412:23 + | +LL | _n.saturating_div(*Box::new(_n)); + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:415:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:413:10 + --> tests/ui/arithmetic_side_effects.rs:416:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:414:15 + --> tests/ui/arithmetic_side_effects.rs:417:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:415:15 + --> tests/ui/arithmetic_side_effects.rs:418:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:424:5 + --> tests/ui/arithmetic_side_effects.rs:419:9 + | +LL | _ = -*Box::new(_n); + | ^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:428:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:5 + --> tests/ui/arithmetic_side_effects.rs:429:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:426:5 + --> tests/ui/arithmetic_side_effects.rs:430:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:427:5 + --> tests/ui/arithmetic_side_effects.rs:431:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:428:5 + --> tests/ui/arithmetic_side_effects.rs:432:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:439:5 + --> tests/ui/arithmetic_side_effects.rs:443:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:440:5 + --> tests/ui/arithmetic_side_effects.rs:444:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:441:5 + --> tests/ui/arithmetic_side_effects.rs:445:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:443:5 + --> tests/ui/arithmetic_side_effects.rs:447:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:445:5 + --> tests/ui/arithmetic_side_effects.rs:449:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:446:5 + --> tests/ui/arithmetic_side_effects.rs:450:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:448:5 + --> tests/ui/arithmetic_side_effects.rs:452:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:450:5 + --> tests/ui/arithmetic_side_effects.rs:454:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:451:5 + --> tests/ui/arithmetic_side_effects.rs:455:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:461:5 + --> tests/ui/arithmetic_side_effects.rs:465:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:515:9 + --> tests/ui/arithmetic_side_effects.rs:519:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:519:9 + --> tests/ui/arithmetic_side_effects.rs:523:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:530:5 + --> tests/ui/arithmetic_side_effects.rs:534:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:534:5 + --> tests/ui/arithmetic_side_effects.rs:538:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 123 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:544:5 + | +LL | one.add(&one); + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:545:5 + | +LL | Box::new(one).add(one); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 128 previous errors From c06ed545dff45d14c70ad412c83cdc600ce67dfc Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 4 Dec 2024 01:42:35 +0300 Subject: [PATCH 106/342] Implement `AtomicT::update` & `AtomicT::try_update` --- library/core/src/sync/atomic.rs | 377 +++++++++++++++++++++++++++++++- 1 file changed, 374 insertions(+), 3 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index fda26a672990..9a26bcc315d3 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1164,7 +1164,7 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1203,6 +1203,125 @@ impl AtomicBool { } Err(prev) } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicBool::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(bool) -> Option, + ) -> Result { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicBool::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), true); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(bool) -> bool, + ) -> bool { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -1684,7 +1803,7 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1732,6 +1851,137 @@ impl AtomicPtr { } Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicPtr::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// assert_eq!(some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + /// let result = some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + /// if x == ptr { + /// Some(new) + /// } else { + /// None + /// } + /// }); + /// assert_eq!(result, Ok(ptr)); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(*mut T) -> Option<*mut T>, + ) -> Result<*mut T, *mut T> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicPtr::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// let result = some_ptr.update(Ordering::SeqCst, Ordering::SeqCst, |_| new); + /// assert_eq!(result, ptr); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(*mut T) -> *mut T, + ) -> *mut T { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } /// Offsets the pointer's address by adding `val` (in units of `T`), /// returning the previous pointer. @@ -2875,7 +3125,7 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] /// and suffers from the same drawbacks. @@ -2913,6 +3163,127 @@ macro_rules! atomic_int { Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + #[doc = concat!("See also: [`update`](`", stringify!($atomic_type), "::update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut($int_type) -> Option<$int_type>, + ) -> Result<$int_type, $int_type> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + #[doc = concat!("See also: [`try_update`](`", stringify!($atomic_type), "::try_update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 7); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 8); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut($int_type) -> $int_type, + ) -> $int_type { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } + /// Maximum with the current value. /// /// Finds the maximum of the current value and the argument `val`, and From 2eef052f04de27abf16e8acdd4f2fa9b144b8b00 Mon Sep 17 00:00:00 2001 From: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com> Date: Wed, 22 Jan 2025 22:21:02 +0100 Subject: [PATCH 107/342] increase `AUTODEREF_RECURSION_LIMIT` to 20 The limit was introduced in https://github.com/rust-lang/rust-analyzer/pull/1408#discussion_r294059044 to avoid infinite cycles but it effectively caps the number of derefs to 10. Types like `ID3D12Device14` from the `windows` crate run into this because it derefs to `ID3D12Device13`, 13 to 12 and so on. Increasing it to 20 is a quick fix; a better cycle detection method would be nicer long term. --- src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2b5342314a65..2c7076da11ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -17,7 +17,7 @@ use crate::{ TraitEnvironment, Ty, TyBuilder, TyKind, }; -static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); +static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20); #[derive(Debug)] pub(crate) enum AutoderefKind { @@ -49,7 +49,7 @@ pub fn autoderef( // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. // - // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't + // XXX: The recursion limit for `Autoderef` is currently 20, so `Vec::contains()` shouldn't // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more // performant. if v.contains(&resolved) { From 5c4e9401dcc553b203cc5b1acee2e7dad2f22e6a Mon Sep 17 00:00:00 2001 From: Boxy Date: Fri, 10 Jan 2025 23:20:31 +0000 Subject: [PATCH 108/342] Make `hir::TyKind::TraitObject` use tagged ptr --- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/types/borrowed_box.rs | 2 +- clippy_lints/src/types/type_complexity.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 2 +- clippy_utils/src/hir_utils.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 239822f40856..b4f6b123472e 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -433,7 +433,7 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); }, - TyKind::TraitObject(bounds, lt, _) => { + TyKind::TraitObject(bounds, lt) => { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index bde88ab61adf..760c5d3ecfed 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -47,7 +47,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Originally reported as the issue #3128. let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { - TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { + TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => { format!("&{ltopt}({inner_snippet})") }, TyKind::Path(qpath) diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index b89bd6a8d058..65830a781b71 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -52,7 +52,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { // function types bring a lot of overhead TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), - TyKind::TraitObject(param_bounds, _, _) => { + TyKind::TraitObject(param_bounds, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound .bound_generic_params diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 68e7f807bf51..026be11aec90 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -400,7 +400,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), - TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")), + TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => (Pat::Str("dyn"), Pat::Str("")), // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index a1c48d5c36cf..bb077f7e4cc4 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1281,7 +1281,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, TyKind::Path(qpath) => self.hash_qpath(qpath), - TyKind::TraitObject(_, lifetime, _) => { + TyKind::TraitObject(_, lifetime) => { self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { From 109440b830a0d7b2a59e6f568803b08e30061cce Mon Sep 17 00:00:00 2001 From: Boxy Date: Fri, 10 Jan 2025 20:11:23 +0000 Subject: [PATCH 109/342] The clipper :3c --- clippy_lints/src/box_default.rs | 17 +++++---- .../src/casts/as_pointer_underscore.rs | 2 +- clippy_lints/src/casts/as_underscore.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 2 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- clippy_lints/src/casts/ptr_as_ptr.rs | 4 +-- clippy_lints/src/casts/ref_as_ptr.rs | 4 +-- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/casts/zero_ptr.rs | 2 +- clippy_lints/src/dereference.rs | 36 ++++++++----------- clippy_lints/src/disallowed_macros.rs | 4 +-- clippy_lints/src/disallowed_types.rs | 4 +-- clippy_lints/src/eta_reduction.rs | 10 +++--- .../src/extra_unused_type_parameters.rs | 12 +++---- clippy_lints/src/from_over_into.rs | 6 ++-- clippy_lints/src/implicit_hasher.rs | 21 ++++------- clippy_lints/src/implied_bounds_in_impls.rs | 10 +++--- clippy_lints/src/let_with_type_underscore.rs | 2 +- clippy_lints/src/lifetimes.rs | 28 +++++++-------- clippy_lints/src/macro_use.rs | 4 +-- clippy_lints/src/manual_bits.rs | 20 +++++------ clippy_lints/src/manual_rem_euclid.rs | 2 +- .../src/methods/unnecessary_lazy_eval.rs | 2 +- .../src/methods/unnecessary_literal_unwrap.rs | 9 ++--- .../src/methods/unused_enumerate_index.rs | 2 +- clippy_lints/src/mut_mut.rs | 5 ++- clippy_lints/src/operators/op_ref.rs | 3 +- clippy_lints/src/ref_option_ref.rs | 4 +-- clippy_lints/src/trait_bounds.rs | 6 ++-- .../missing_transmute_annotations.rs | 3 +- clippy_lints/src/types/borrowed_box.rs | 2 +- clippy_lints/src/types/mod.rs | 6 ++-- clippy_lints/src/types/type_complexity.rs | 18 +++++----- clippy_lints/src/types/vec_box.rs | 6 ++-- clippy_lints/src/unit_types/let_unit_value.rs | 4 +-- clippy_lints/src/use_self.rs | 25 +++++++------ clippy_lints/src/zero_sized_map_values.rs | 7 ++-- clippy_utils/src/check_proc_macro.rs | 2 +- clippy_utils/src/hir_utils.rs | 12 +++---- clippy_utils/src/lib.rs | 4 +-- clippy_utils/src/ty/type_certainty/mod.rs | 15 ++++---- clippy_utils/src/visitors.rs | 6 ++-- 42 files changed, 167 insertions(+), 170 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index a1ca23e65ff1..f05817cc71d9 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -4,12 +4,12 @@ use clippy_utils::ty::expr_sig; use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{Visitor, walk_ty}; -use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind}; +use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; +use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -92,8 +92,13 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) struct InferVisitor(bool); impl Visitor<'_> for InferVisitor { - fn visit_ty(&mut self, t: &Ty<'_>) { - self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); + fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, _kind: InferKind<'_>) -> Self::Result { + self.0 = true; + self.visit_id(inf_id); + } + + fn visit_ty(&mut self, t: &Ty<'_, AmbigArg>) { + self.0 |= matches!(t.kind, TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); if !self.0 { walk_ty(self, t); } @@ -104,7 +109,7 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(LetStmt { ty: Some(ty), .. }) => { let mut v = InferVisitor::default(); - v.visit_ty(ty); + v.visit_unambig_ty(ty); !v.0 }, Node::Expr(Expr { diff --git a/clippy_lints/src/casts/as_pointer_underscore.rs b/clippy_lints/src/casts/as_pointer_underscore.rs index 536126fd02b3..3ab6693756f5 100644 --- a/clippy_lints/src/casts/as_pointer_underscore.rs +++ b/clippy_lints/src/casts/as_pointer_underscore.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::Ty; pub fn check<'tcx>(cx: &LateContext<'tcx>, ty_into: Ty<'_>, cast_to_hir: &'tcx rustc_hir::Ty<'tcx>) { if let rustc_hir::TyKind::Ptr(rustc_hir::MutTy { ty, .. }) = cast_to_hir.kind - && matches!(ty.kind, rustc_hir::TyKind::Infer) + && matches!(ty.kind, rustc_hir::TyKind::Infer(())) { clippy_utils::diagnostics::span_lint_and_sugg( cx, diff --git a/clippy_lints/src/casts/as_underscore.rs b/clippy_lints/src/casts/as_underscore.rs index 56e894c6261e..3ac486dd63fb 100644 --- a/clippy_lints/src/casts/as_underscore.rs +++ b/clippy_lints/src/casts/as_underscore.rs @@ -7,7 +7,7 @@ use rustc_middle::ty; use super::AS_UNDERSCORE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) { - if matches!(ty.kind, TyKind::Infer) { + if matches!(ty.kind, TyKind::Infer(())) { span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| { let ty_resolved = cx.typeck_results().expr_ty(expr); if let ty::Error(_) = ty_resolved.kind() { diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 4ad39d9160de..c326a0d935c7 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -38,7 +38,7 @@ pub(super) fn check( return; }; match cast_to_hir.kind { - TyKind::Infer => { + TyKind::Infer(()) => { diag.span_suggestion_verbose( expr.span, "use `Into::into` instead", diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 205357afd840..e4c0db5d9ef0 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args // There probably is no obvious reason to do this, just to be consistent with `as` cases. - && !is_hir_ty_cfg_dependant(cx, cast_to) + && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty()) { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index a138ade54aad..bdc389d39dd3 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -43,9 +43,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { { let mut app = Applicability::MachineApplicable; let turbofish = match &cast_to_hir_ty.kind { - TyKind::Infer => String::new(), + TyKind::Infer(()) => String::new(), TyKind::Ptr(mut_ty) => { - if matches!(mut_ty.ty.kind, TyKind::Infer) { + if matches!(mut_ty.ty.kind, TyKind::Infer(())) { String::new() } else { format!( diff --git a/clippy_lints/src/casts/ref_as_ptr.rs b/clippy_lints/src/casts/ref_as_ptr.rs index f699bba20ed0..592c820a25e1 100644 --- a/clippy_lints/src/casts/ref_as_ptr.rs +++ b/clippy_lints/src/casts/ref_as_ptr.rs @@ -34,9 +34,9 @@ pub(super) fn check<'tcx>( let mut app = Applicability::MachineApplicable; let turbofish = match &cast_to_hir_ty.kind { - TyKind::Infer => String::new(), + TyKind::Infer(()) => String::new(), TyKind::Ptr(mut_ty) => { - if matches!(mut_ty.ty.kind, TyKind::Infer) { + if matches!(mut_ty.ty.kind, TyKind::Infer(())) { String::new() } else { format!( diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 332e897def7e..9e1876e40f99 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>( } }, // Ignore `p as *const _` - TyKind::Infer => return false, + TyKind::Infer(()) => return false, _ => {}, } diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs index c5c4a28646d2..a34af6bc226c 100644 --- a/clippy_lints/src/casts/zero_ptr.rs +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -18,7 +18,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_> Mutability::Not => ("`0 as *const _` detected", "ptr::null"), }; - let sugg = if let TyKind::Infer = mut_ty.ty.kind { + let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 653726872c64..1ac545765dde 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -11,10 +11,10 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, - PatKind, Path, QPath, TyKind, UnOp, + self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, + Pat, PatKind, Path, QPath, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -796,7 +796,7 @@ impl TyCoercionStability { if let Some(args) = path.args && args.args.iter().any(|arg| match arg { hir::GenericArg::Infer(_) => true, - hir::GenericArg::Type(ty) => ty_contains_infer(ty), + hir::GenericArg::Type(ty) => ty_contains_infer(ty.as_unambig_ty()), _ => false, }) { @@ -815,7 +815,7 @@ impl TyCoercionStability { | TyKind::Path(_) => Self::Deref, TyKind::OpaqueDef(..) | TyKind::TraitAscription(..) - | TyKind::Infer + | TyKind::Infer(()) | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::InferDelegation(..) @@ -889,29 +889,23 @@ impl TyCoercionStability { fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { struct V(bool); impl Visitor<'_> for V { - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - if self.0 - || matches!( - ty.kind, - TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err(_) - ) - { + fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, kind: InferKind<'_>) -> Self::Result { + if let InferKind::Ty(_) | InferKind::Ambig(_) = kind { + self.0 = true; + } + self.visit_id(inf_id); + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_, AmbigArg>) { + if self.0 || matches!(ty.kind, TyKind::OpaqueDef(..) | TyKind::Typeof(_) | TyKind::Err(_)) { self.0 = true; } else { walk_ty(self, ty); } } - - fn visit_generic_arg(&mut self, arg: &hir::GenericArg<'_>) { - if self.0 || matches!(arg, hir::GenericArg::Infer(_)) { - self.0 = true; - } else if let hir::GenericArg::Type(ty) = arg { - self.visit_ty(ty); - } - } } let mut v = V(false); - v.visit_ty(ty); + v.visit_unambig_ty(ty); v.0 } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index a78c392e2086..4e8853821c3e 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Diag; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; @@ -140,7 +140,7 @@ impl LateLintPass<'_> for DisallowedMacros { self.check(cx, stmt.span, None); } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_, AmbigArg>) { self.check(cx, ty.span, None); } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 3265404f2b27..947677e14bd0 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; +use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { } } - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index c0b4743fd71c..57a30de7ad14 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -76,22 +76,22 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind { for arg in args { - check_clousure(cx, Some(receiver), arg); + check_closure(cx, Some(receiver), arg); } } if let ExprKind::Call(func, args) = expr.kind { - check_clousure(cx, None, func); + check_closure(cx, None, func); for arg in args { - check_clousure(cx, None, arg); + check_closure(cx, None, arg); } } } } #[allow(clippy::too_many_lines)] -fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { +fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { let body = if let ExprKind::Closure(c) = expr.kind - && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) + && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) && !expr.span.from_expansion() { diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index d0159ab89e10..150240683350 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -3,10 +3,10 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty}; +use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty, walk_unambig_ty}; use rustc_hir::{ - BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, - PredicateOrigin, Ty, WherePredicate, WherePredicateKind, + AmbigArg, BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, + ItemKind, PredicateOrigin, Ty, WherePredicate, WherePredicateKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; @@ -196,8 +196,8 @@ fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option { impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { - if let Some((def_id, _)) = t.peel_refs().as_generic_param() { + fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) { + if let Some((def_id, _)) = t.as_unambig_ty().peel_refs().as_generic_param() { self.ty_params.remove(&def_id); } else { walk_ty(self, t); @@ -234,7 +234,7 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { // type, any params we find nested inside of it are being used as concrete types, // and can therefore can be considered used. So, we're fine to walk the left-hand // side of the where bound. - walk_ty(self, predicate.bounded_ty); + walk_unambig_ty(self, predicate.bounded_ty); } for bound in predicate.bounds { walk_param_bound(self, bound); diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e43c311eb854..68495441f3bb 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { |diag| { // If the target type is likely foreign mention the orphan rules as it's a common source of // confusion - if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) { + if path_def_id(cx, target_ty.as_unambig_ty().peel_refs()).is_none_or(|id| !id.is_local()) { diag.help( "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" @@ -103,7 +103,9 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { "replace the `Into` implementation with `From<{}>`", middle_trait_ref.self_ty() ); - if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) { + if let Some(suggestions) = + convert_to_from(cx, into_trait_seg, target_ty.as_unambig_ty(), self_ty, impl_item_ref) + { diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); } else { diag.help(message); diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index ad2da3c7fcd1..34cacda06ff8 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -2,9 +2,8 @@ use std::borrow::Cow; use std::collections::BTreeMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir as hir; -use rustc_hir::intravisit::{Visitor, walk_body, walk_expr, walk_inf, walk_ty}; -use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty, walk_body, walk_expr}; +use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -112,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { match item.kind { ItemKind::Impl(impl_) => { let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_ty(impl_.self_ty); + vis.visit_unambig_ty(impl_.self_ty); for target in &vis.found { if !item.span.eq_ctxt(target.span()) { @@ -159,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { for ty in sig.decl.inputs { let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_ty(ty); + vis.visit_unambig_ty(ty); for target in &vis.found { if generics.span.from_expansion() { @@ -287,21 +286,13 @@ impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> { } impl<'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'_, 'tcx> { - fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) { - if let Some(target) = ImplicitHasherType::new(self.cx, t) { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'_, AmbigArg>) { + if let Some(target) = ImplicitHasherType::new(self.cx, t.as_unambig_ty()) { self.found.push(target); } walk_ty(self, t); } - - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - if let Some(target) = ImplicitHasherType::new(self.cx, &inf.to_ty()) { - self.found.push(target); - } - - walk_inf(self, inf); - } } /// Looks for default-hasher-dependent constructors like `HashMap::new`. diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index ef272c305d34..d02d9b2102bd 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -3,8 +3,8 @@ use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ - AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind, - WherePredicateKind, + AmbigArg, AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, + TyKind, WherePredicateKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -146,7 +146,9 @@ fn try_resolve_type<'tcx>( index: usize, ) -> Option> { match args.get(index - 1) { - Some(GenericArg::Type(ty)) => Some(lower_ty(tcx, ty)), + // I don't think we care about `GenericArg::Infer` since this is all for stuff in type signatures + // which do not permit inference variables. + Some(GenericArg::Type(ty)) => Some(lower_ty(tcx, ty.as_unambig_ty())), Some(_) => None, None => Some(tcx.type_of(generics.own_params[index].def_id).skip_binder()), } @@ -335,7 +337,7 @@ impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls { } } - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx, AmbigArg>) { if let TyKind::OpaqueDef(opaque_ty, ..) = ty.kind { check(cx, opaque_ty.bounds); } diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 5a11702d7ce5..2d2438514cc9 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -28,7 +28,7 @@ declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { if let Some(ty) = local.ty // Ensure that it has a type defined - && let TyKind::Infer = &ty.kind // that type is '_' + && let TyKind::Infer(()) = &ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) && !in_external_macro(cx.tcx.sess, local.span) && !is_from_proc_macro(cx, ty) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index b4f6b123472e..44580cb38d76 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -7,13 +7,13 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref, - walk_trait_ref, walk_ty, walk_where_predicate, + Visitor, VisitorExt, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate, }; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, - HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, - PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, + AmbigArg, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, + Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, + PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, WherePredicateKind, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -232,11 +232,11 @@ fn could_use_elision<'tcx>( // extract lifetimes in input argument types for arg in func.inputs { - input_visitor.visit_ty(arg); + input_visitor.visit_unambig_ty(arg); } // extract lifetimes in output type if let Return(ty) = func.output { - output_visitor.visit_ty(ty); + output_visitor.visit_unambig_ty(ty); } for lt in named_generics { input_visitor.visit_generic_param(lt); @@ -340,7 +340,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: && let Some(self_ty) = func.inputs.first() { let mut visitor = RefVisitor::new(cx); - visitor.visit_ty(self_ty); + visitor.visit_unambig_ty(self_ty); !visitor.all_lts().is_empty() } else { @@ -426,7 +426,7 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { } } - fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { + fn visit_ty(&mut self, ty: &'tcx Ty<'_, AmbigArg>) { match ty.kind { TyKind::BareFn(&BareFnTy { decl, .. }) => { let mut sub_visitor = RefVisitor::new(self.cx); @@ -456,7 +456,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ // a predicate like F: Trait or F: for<'a> Trait<'a> let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs - walk_ty(&mut visitor, pred.bounded_ty); + walk_unambig_ty(&mut visitor, pred.bounded_ty); if !visitor.all_lts().is_empty() { return true; } @@ -477,8 +477,8 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ }, WherePredicateKind::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); - walk_ty(&mut visitor, pred.lhs_ty); - walk_ty(&mut visitor, pred.rhs_ty); + walk_unambig_ty(&mut visitor, pred.lhs_ty); + walk_unambig_ty(&mut visitor, pred.rhs_ty); if !visitor.lts.is_empty() { return true; } @@ -541,7 +541,7 @@ where try_visit!(self.visit_id(hir_id)); self.bounded_ty_depth += 1; - try_visit!(self.visit_ty(bounded_ty)); + try_visit!(self.visit_unambig_ty(bounded_ty)); self.bounded_ty_depth -= 1; walk_list!(self, visit_param_bound, bounds); @@ -625,7 +625,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' if let Some(ref trait_ref) = impl_.of_trait { walk_trait_ref(&mut checker, trait_ref); } - walk_ty(&mut checker, impl_.self_ty); + walk_unambig_ty(&mut checker, impl_.self_ty); for item in impl_.items { walk_impl_item_ref(&mut checker, item); } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 3c669d94d693..374128665392 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition; @@ -123,7 +123,7 @@ impl LateLintPass<'_> for MacroUseImports { self.push_unique_macro_pat_ty(cx, pat.span); } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_, AmbigArg>) { if ty.span.from_expansion() { self.push_unique_macro_pat_ty(cx, ty.span); } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index c31656f8a05e..d2a2321dae80 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -6,11 +6,11 @@ use clippy_utils::source::snippet_with_context; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -57,13 +57,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { && let ctxt = expr.span.ctxt() && left_expr.span.ctxt() == ctxt && right_expr.span.ctxt() == ctxt - && let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr) + && let Some((real_ty_span, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr) && matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)) && let ExprKind::Lit(lit) = &other_expr.kind && let LitKind::Int(Pu128(8), _) = lit.node { let mut app = Applicability::MachineApplicable; - let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; + let ty_snip = snippet_with_context(cx, real_ty_span, ctxt, "..", &mut app).0; let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); span_lint_and_sugg( @@ -85,21 +85,21 @@ fn get_one_size_of_ty<'tcx>( cx: &LateContext<'tcx>, expr1: &'tcx Expr<'_>, expr2: &'tcx Expr<'_>, -) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> { +) -> Option<(Span, Ty<'tcx>, &'tcx Expr<'tcx>)> { match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) { - (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)), - (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)), + (Some((real_ty_span, resolved_ty)), None) => Some((real_ty_span, resolved_ty, expr2)), + (None, Some((real_ty_span, resolved_ty))) => Some((real_ty_span, resolved_ty, expr1)), _ => None, } } -fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Span, Ty<'tcx>)> { if let ExprKind::Call(count_func, []) = expr.kind && let ExprKind::Path(ref count_func_qpath) = count_func.kind && let QPath::Resolved(_, count_func_path) = count_func_qpath && let Some(segment_zero) = count_func_path.segments.first() && let Some(args) = segment_zero.args - && let Some(GenericArg::Type(real_ty)) = args.args.first() + && let Some(real_ty_span) = args.args.first().map(|arg| arg.span()) && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) { @@ -107,7 +107,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< .node_args(count_func.hir_id) .types() .next() - .map(|resolved_ty| (*real_ty, resolved_ty)) + .map(|resolved_ty| (real_ty_span, resolved_ty)) } else { None } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 5e58054a9866..ebfd946b07ee 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { Node::Param(..) => (), Node::LetStmt(local) => { let Some(ty) = local.ty else { return }; - if matches!(ty.kind, TyKind::Infer) { + if matches!(ty.kind, TyKind::Infer(())) { return; } }, diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index b84594c0da19..1673a6f8b3a4 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>( fn_decl.output, FnRetTy::DefaultReturn(_) | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer, + kind: hir::TyKind::Infer(()), .. }) ) { diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 6dc8adb42dfb..10112b62878a 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; use super::UNNECESSARY_LITERAL_UNWRAP; -fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a>> { +fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a, AmbigArg>> { let args = args?; if args.len() <= index { @@ -16,10 +16,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) - } match args[index] { - hir::GenericArg::Type(ty) => match ty.kind { - hir::TyKind::Infer => None, - _ => Some(ty), - }, + hir::GenericArg::Type(ty) => Some(ty), _ => None, } } diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index 6c2ae9cc6bff..0aec26f10111 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -130,7 +130,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option { if let [tuple_ty] = fn_decl.inputs && let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind - && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer) + && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer(())) { Some(elem_ty.span) } else { diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index e2ab5e98504a..0ee851a4cf94 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_hir}; use clippy_utils::higher; -use rustc_hir as hir; -use rustc_hir::intravisit; +use rustc_hir::{self as hir, AmbigArg, intravisit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -34,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { intravisit::walk_block(&mut MutVisitor { cx }, block); } - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_, AmbigArg>) { if let hir::TyKind::Ref(_, mty) = ty.kind && mty.mutbl == hir::Mutability::Mut && let hir::TyKind::Ref(_, mty) = mty.ty.kind diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 82b9d10fbeb1..c3c09946c27d 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -190,7 +190,8 @@ fn in_impl<'tcx>( && let Some(generic_args) = seg.args && let Some(GenericArg::Type(other_ty)) = generic_args.args.last() { - Some((item.self_ty, other_ty)) + // `_` is not permitted in impl headers + Some((item.self_ty, other_ty.as_unambig_ty())) } else { None } diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 7d59bf24d937..074345e75321 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::last_path_segment; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind}; +use rustc_hir::{AmbigArg, GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Ref(_, ref mut_ty) = ty.kind && mut_ty.mutbl == Mutability::Not && let TyKind::Path(qpath) = &mut_ty.ty.kind diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 99844beb8f00..790e0965198d 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -10,8 +10,8 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, - TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicateKind, + AmbigArg, BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, + PredicateOrigin, QPath, TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicateKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -171,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Ref(.., mut_ty) = &ty.kind && let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind && bounds.len() > 2 diff --git a/clippy_lints/src/transmute/missing_transmute_annotations.rs b/clippy_lints/src/transmute/missing_transmute_annotations.rs index b2892d136fa3..531422798a68 100644 --- a/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -52,7 +52,6 @@ pub(super) fn check<'tcx>( let missing_generic = match args { Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| match arg { GenericArg::Infer(_) => true, - GenericArg::Type(ty) => matches!(ty.kind, TyKind::Infer), _ => false, }), _ => true, @@ -65,7 +64,7 @@ pub(super) fn check<'tcx>( // ... which does have type annotations. if let Some(ty) = local.ty // If this is a `let x: _ =`, we should lint. - && !matches!(ty.kind, TyKind::Infer) + && !matches!(ty.kind, TyKind::Infer(())) { return false; } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 760c5d3ecfed..1a5fdf0cd64f 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m _ => None, }) { - if is_any_trait(cx, inner) { + if is_any_trait(cx, inner.as_unambig_ty()) { // Ignore `Box` types; see issue #1884 for details. return false; } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 43cce625c641..391c36df492c 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -560,7 +560,7 @@ impl Types { _ => None, }) }) { - self.check_ty(cx, ty, context); + self.check_ty(cx, ty.as_unambig_ty(), context); } }, QPath::Resolved(None, p) => { @@ -574,7 +574,7 @@ impl Types { _ => None, }) }) { - self.check_ty(cx, ty, context); + self.check_ty(cx, ty.as_unambig_ty(), context); } }, QPath::TypeRelative(ty, seg) => { @@ -585,7 +585,7 @@ impl Types { GenericArg::Type(ty) => Some(ty), _ => None, }) { - self.check_ty(cx, ty, context); + self.check_ty(cx, ty.as_unambig_ty(), context); } } }, diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 65830a781b71..08b2e9e3b019 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use rustc_hir as hir; -use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty}; -use rustc_hir::{GenericParamKind, TyKind}; +use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; +use rustc_hir::{self as hir, AmbigArg, GenericParamKind, TyKind}; use rustc_lint::LateContext; +use rustc_span::Span; use rustc_target::spec::abi::Abi; use super::TYPE_COMPLEXITY; @@ -10,7 +10,7 @@ use super::TYPE_COMPLEXITY; pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool { let score = { let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; - visitor.visit_ty(ty); + visitor.visit_unambig_ty(ty); visitor.score }; @@ -36,15 +36,15 @@ struct TypeComplexityVisitor { } impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { + fn visit_infer(&mut self, inf_id: hir::HirId, _inf_span: Span, _kind: InferKind<'tcx>) -> Self::Result { self.score += 1; - walk_inf(self, inf); + self.visit_id(inf_id); } - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_, AmbigArg>) { let (add_score, sub_nest) = match ty.kind { - // _, &x and *x have only small overhead; don't mess with nesting level - TyKind::Infer | TyKind::Ptr(..) | TyKind::Ref(..) => (1, 0), + // &x and *x have only small overhead; don't mess with nesting level + TyKind::Ptr(..) | TyKind::Ref(..) => (1, 0), // the "normal" components of a type: named types, arrays/tuples TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 9b236d3bda55..769244c675e1 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -35,7 +35,8 @@ pub(super) fn check<'tcx>( && let Some(GenericArg::Type(boxed_ty)) = last.args.first() // extract allocator from the Box for later && let boxed_alloc_ty = last.args.get(1) - && let ty_ty = lower_ty(cx.tcx, boxed_ty) + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty()) && !ty_ty.has_escaping_bound_vars() && ty_ty.is_sized(cx.tcx, cx.typing_env()) && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) @@ -55,7 +56,8 @@ pub(super) fn check<'tcx>( } }, (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => - lower_ty(cx.tcx, l) == lower_ty(cx.tcx, r), + // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay + lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()), _ => false } { diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index d2727968c0c9..660bdb9e2be2 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { return; } - if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer)) + if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer(()))) || matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none())) && expr_needs_inferred_result(cx, init) { @@ -158,7 +158,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) - } while let Some(id) = locals_to_check.pop() { if let Node::LetStmt(l) = cx.tcx.parent_hir_node(id) { - if !l.ty.is_none_or(|ty| matches!(ty.kind, TyKind::Infer)) { + if !l.ty.is_none_or(|ty| matches!(ty.kind, TyKind::Infer(()))) { return false; } if let Some(e) = l.init { diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 05c5be030028..298efac0cb53 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -7,10 +7,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty}; +use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, - ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, + HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); - visitor.visit_ty(impl_hir_ty); + visitor.visit_unambig_ty(impl_hir_ty); types_to_skip.extend(visitor.types_to_skip); } } @@ -201,7 +201,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx, AmbigArg>) { if !hir_ty.span.from_expansion() && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) && let Some(&StackItem::Check { @@ -218,7 +218,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && let ty = if in_body > 0 { cx.typeck_results().node_type(hir_ty.hir_id) } else { - lower_ty(cx.tcx, hir_ty) + // We don't care about ignoring infer vars here + lower_ty(cx.tcx, hir_ty.as_unambig_ty()) } && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() && same_type_and_consts(ty, impl_ty) @@ -275,12 +276,14 @@ struct SkipTyCollector { } impl Visitor<'_> for SkipTyCollector { - fn visit_infer(&mut self, inf: &hir::InferArg) { - self.types_to_skip.push(inf.hir_id); - - walk_inf(self, inf); + fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, kind: InferKind<'_>) -> Self::Result { + // Conservatively assume ambiguously kinded inferred arguments are type arguments + if let InferKind::Ambig(_) | InferKind::Ty(_) = kind { + self.types_to_skip.push(inf_id); + } + self.visit_id(inf_id); } - fn visit_ty(&mut self, hir_ty: &Ty<'_>) { + fn visit_ty(&mut self, hir_ty: &Ty<'_, AmbigArg>) { self.types_to_skip.push(hir_ty.hir_id); walk_ty(self, hir_ty); diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index e14480b86556..1221abec1abf 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; -use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; @@ -44,10 +44,11 @@ declare_clippy_lint! { declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); impl LateLintPass<'_> for ZeroSizedMapValues { - fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) { + fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx, AmbigArg>) { if !hir_ty.span.from_expansion() && !in_trait_impl(cx, hir_ty.hir_id) - && let ty = ty_from_hir_ty(cx, hir_ty) + // We don't care about infer vars + && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 026be11aec90..cd6290ced334 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -399,7 +399,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), - TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), + TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => (Pat::Str("dyn"), Pat::Str("")), // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index bb077f7e4cc4..d76231a6eeaa 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -459,9 +459,9 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool { match (left, right) { - (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_const_arg(l, r), + (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_const_arg(l.as_unambig_ct(), r.as_unambig_ct()), (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt), - (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty), + (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty.as_unambig_ty(), r_ty.as_unambig_ty()), (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()), _ => false, } @@ -618,7 +618,7 @@ impl HirEqInterExpr<'_, '_, '_> { }, (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), - (&TyKind::Infer, &TyKind::Infer) => true, + (&TyKind::Infer(()), &TyKind::Infer(())) => true, _ => false, } } @@ -1291,7 +1291,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(binder.inner_ty); }, TyKind::Err(_) - | TyKind::Infer + | TyKind::Infer(()) | TyKind::Never | TyKind::InferDelegation(..) | TyKind::OpaqueDef(_) @@ -1318,8 +1318,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { for arg in arg_list { match *arg { GenericArg::Lifetime(l) => self.hash_lifetime(l), - GenericArg::Type(ty) => self.hash_ty(ty), - GenericArg::Const(ca) => self.hash_const_arg(ca), + GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()), + GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()), GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()), } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index eecfc3fb13f8..93e3fb36b352 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -437,7 +437,7 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator Some(*ty), + hir::GenericArg::Type(ty) => Some(ty.as_unambig_ty()), _ => None, }) } @@ -2148,7 +2148,7 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(&Closure { body, fn_decl, .. }) - if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) => + if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) => { is_body_identity_function(cx, cx.tcx.hir().body(body)) }, diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index d7640ebfb006..ccb33f7e500a 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -14,8 +14,8 @@ use crate::def_path_res; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{Visitor, walk_qpath, walk_ty}; -use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; +use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; use rustc_span::{Span, Symbol}; @@ -116,14 +116,15 @@ impl<'cx> Visitor<'cx> for CertaintyVisitor<'cx, '_> { } } - fn visit_ty(&mut self, ty: &'cx hir::Ty<'_>) { - if matches!(ty.kind, TyKind::Infer) { - self.certainty = Certainty::Uncertain; - } + fn visit_ty(&mut self, ty: &'cx hir::Ty<'_, AmbigArg>) { if self.certainty != Certainty::Uncertain { walk_ty(self, ty); } } + + fn visit_infer(&mut self, _inf_id: HirId, _inf_span: Span, _kind: InferKind<'cx>) -> Self::Result { + self.certainty = Certainty::Uncertain; + } } fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty { @@ -139,7 +140,7 @@ fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty { } let mut visitor = CertaintyVisitor::new(cx); - visitor.visit_ty(ty); + visitor.visit_unambig_ty(ty); visitor.certainty } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 7a3a861a9cae..dcc763a8abd7 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -2,7 +2,7 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; -use rustc_hir as hir; +use rustc_hir::{self as hir, AmbigArg}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ @@ -122,7 +122,7 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { ControlFlow::Continue(()) } fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result { @@ -172,7 +172,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( ControlFlow::Continue(()) } // Avoid unnecessary `walk_*` calls. - fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { ControlFlow::Continue(()) } fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result { From 6ed958869dec5fe9dfef89622ec6923d514f3148 Mon Sep 17 00:00:00 2001 From: Boxy Date: Sat, 18 Jan 2025 22:45:41 +0000 Subject: [PATCH 110/342] `visit_x_unambig` --- clippy_lints/src/box_default.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/implicit_hasher.rs | 6 +++--- clippy_lints/src/lifetimes.rs | 8 ++++---- clippy_lints/src/types/type_complexity.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index f05817cc71d9..79fd6ffe46c3 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -109,7 +109,7 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(LetStmt { ty: Some(ty), .. }) => { let mut v = InferVisitor::default(); - v.visit_unambig_ty(ty); + v.visit_ty_unambig(ty); !v.0 }, Node::Expr(Expr { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 1ac545765dde..f5589d8f8e25 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -905,7 +905,7 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { } } let mut v = V(false); - v.visit_unambig_ty(ty); + v.visit_ty_unambig(ty); v.0 } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 34cacda06ff8..47a5c19215b8 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty, walk_body, walk_expr}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_body, walk_expr, walk_ty}; use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { match item.kind { ItemKind::Impl(impl_) => { let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_unambig_ty(impl_.self_ty); + vis.visit_ty_unambig(impl_.self_ty); for target in &vis.found { if !item.span.eq_ctxt(target.span()) { @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { for ty in sig.decl.inputs { let mut vis = ImplicitHasherTypeVisitor::new(cx); - vis.visit_unambig_ty(ty); + vis.visit_ty_unambig(ty); for target in &vis.found { if generics.span.from_expansion() { diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 44580cb38d76..e6761ea5c67c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -232,11 +232,11 @@ fn could_use_elision<'tcx>( // extract lifetimes in input argument types for arg in func.inputs { - input_visitor.visit_unambig_ty(arg); + input_visitor.visit_ty_unambig(arg); } // extract lifetimes in output type if let Return(ty) = func.output { - output_visitor.visit_unambig_ty(ty); + output_visitor.visit_ty_unambig(ty); } for lt in named_generics { input_visitor.visit_generic_param(lt); @@ -340,7 +340,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: && let Some(self_ty) = func.inputs.first() { let mut visitor = RefVisitor::new(cx); - visitor.visit_unambig_ty(self_ty); + visitor.visit_ty_unambig(self_ty); !visitor.all_lts().is_empty() } else { @@ -541,7 +541,7 @@ where try_visit!(self.visit_id(hir_id)); self.bounded_ty_depth += 1; - try_visit!(self.visit_unambig_ty(bounded_ty)); + try_visit!(self.visit_ty_unambig(bounded_ty)); self.bounded_ty_depth -= 1; walk_list!(self, visit_param_bound, bounds); diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 08b2e9e3b019..7f51660293b5 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -10,7 +10,7 @@ use super::TYPE_COMPLEXITY; pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool { let score = { let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 }; - visitor.visit_unambig_ty(ty); + visitor.visit_ty_unambig(ty); visitor.score }; diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 298efac0cb53..84b6430294f3 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); - visitor.visit_unambig_ty(impl_hir_ty); + visitor.visit_ty_unambig(impl_hir_ty); types_to_skip.extend(visitor.types_to_skip); } } diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index ccb33f7e500a..b5cec31ba9da 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -140,7 +140,7 @@ fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty { } let mut visitor = CertaintyVisitor::new(cx); - visitor.visit_unambig_ty(ty); + visitor.visit_ty_unambig(ty); visitor.certainty } From 3309f0296f7c7f997c30a52f74e746f74b1cc7bd Mon Sep 17 00:00:00 2001 From: Boxy Date: Sun, 19 Jan 2025 00:16:39 +0000 Subject: [PATCH 111/342] make `hir::Ty/ConstArg` methods generic where applicable --- clippy_lints/src/extra_unused_type_parameters.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index 150240683350..688979311c89 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) { - if let Some((def_id, _)) = t.as_unambig_ty().peel_refs().as_generic_param() { + if let Some((def_id, _)) = t.peel_refs().as_generic_param() { self.ty_params.remove(&def_id); } else { walk_ty(self, t); diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 68495441f3bb..9a73d0c0993f 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { |diag| { // If the target type is likely foreign mention the orphan rules as it's a common source of // confusion - if path_def_id(cx, target_ty.as_unambig_ty().peel_refs()).is_none_or(|id| !id.is_local()) { + if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) { diag.help( "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" From 2f2d09a858735662f1f53a20629cda4f7c69c3b7 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Mon, 20 Jan 2025 06:06:11 -0500 Subject: [PATCH 112/342] clarify message for non-obvious precedence --- clippy_lints/src/precedence.rs | 2 +- tests/ui/precedence.stderr | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 031f09310590..421b2b747555 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -43,7 +43,7 @@ impl EarlyLintPass for Precedence { cx, PRECEDENCE, expr.span, - "operator precedence can trip the unwary", + "operator precedence might not be obvious", "consider parenthesizing your expression", sugg, appl, diff --git a/tests/ui/precedence.stderr b/tests/ui/precedence.stderr index 0d63e827d66e..329422cb8a69 100644 --- a/tests/ui/precedence.stderr +++ b/tests/ui/precedence.stderr @@ -1,4 +1,4 @@ -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:16:5 | LL | 1 << 2 + 3; @@ -7,61 +7,61 @@ LL | 1 << 2 + 3; = note: `-D clippy::precedence` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::precedence)]` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:17:5 | LL | 1 + 2 << 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:18:5 | LL | 4 >> 1 + 1; | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:19:5 | LL | 1 + 3 >> 2; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:20:5 | LL | 1 ^ 1 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:21:5 | LL | 3 | 2 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:22:5 | LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:23:5 | LL | 0x0F00 & 0x00F0 << 4; | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:24:5 | LL | 0x0F00 & 0xF000 >> 4; | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:25:5 | LL | 0x0F00 << 1 ^ 3; | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:26:5 | LL | 0x0F00 << 1 | 2; From 1da4eb3f8b878ad1d93948a91333baf83347e03f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:58:47 +0000 Subject: [PATCH 113/342] Remove the need to manually call set_using_internal_features --- src/driver.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 75ef60a5dc8a..b3bfec427200 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -186,7 +186,7 @@ pub fn main() { rustc_driver::init_rustc_env_logger(&early_dcx); - let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |dcx| { + rustc_driver::install_ice_hook(BUG_REPORT_URL, |dcx| { // FIXME: this macro calls unwrap internally but is called in a panicking context! It's not // as simple as moving the call from the hook to main, because `install_ice_hook` doesn't // accept a generic closure. @@ -295,13 +295,9 @@ pub fn main() { let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { args.extend(clippy_args); - rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }) - .set_using_internal_features(using_internal_features) - .run(); + rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run(); } else { - rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }) - .set_using_internal_features(using_internal_features) - .run(); + rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run(); } Ok(()) })) From d7b39401dd98b771fd508a252fc96937aec0eef3 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:13:14 +0000 Subject: [PATCH 114/342] Remove RunCompiler It has become nothing other than a wrapper around run_compiler. --- src/driver.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index b3bfec427200..68edefd3095c 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -236,7 +236,7 @@ pub fn main() { let mut args: Vec = orig_args.clone(); pass_sysroot_env_if_given(&mut args, sys_root_env); - rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); + rustc_driver::run_compiler(&args, &mut DefaultCallbacks); return Ok(()); } @@ -295,9 +295,9 @@ pub fn main() { let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { args.extend(clippy_args); - rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run(); + rustc_driver::run_compiler(&args, &mut ClippyCallbacks { clippy_args_var }); } else { - rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run(); + rustc_driver::run_compiler(&args, &mut RustcCallbacks { clippy_args_var }); } Ok(()) })) From ccb967438de778b7f4ee3cf94f3e8163f6f5c53f Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 22 Jan 2025 16:01:10 +0100 Subject: [PATCH 115/342] simplify similar_tokens from Option> to Vec<_> --- compiler/rustc_ast/src/token.rs | 14 +++++++------- compiler/rustc_builtin_macros/src/format.rs | 15 +++++++-------- compiler/rustc_parse/src/parser/expr.rs | 3 +-- compiler/rustc_parse/src/parser/mod.rs | 6 ++---- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 3b7367d1ee20..5b6e65c804f5 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -527,13 +527,13 @@ impl TokenKind { /// Returns tokens that are likely to be typed accidentally instead of the current token. /// Enables better error recovery when the wrong token is found. - pub fn similar_tokens(&self) -> Option> { - match *self { - Comma => Some(vec![Dot, Lt, Semi]), - Semi => Some(vec![Colon, Comma]), - Colon => Some(vec![Semi]), - FatArrow => Some(vec![Eq, RArrow, Ge, Gt]), - _ => None, + pub fn similar_tokens(&self) -> Vec { + match self { + Comma => vec![Dot, Lt, Semi], + Semi => vec![Colon, Comma], + Colon => vec![Semi], + FatArrow => vec![Eq, RArrow, Ge, Gt], + _ => vec![], } } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5202fe26c401..d9cfbaf63e62 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -101,15 +101,14 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, match p.expect(exp!(Comma)) { Err(err) => { - match token::TokenKind::Comma.similar_tokens() { - Some(tks) if tks.contains(&p.token.kind) => { - // If a similar token is found, then it may be a typo. We - // consider it as a comma, and continue parsing. - err.emit(); - p.bump(); - } + if token::TokenKind::Comma.similar_tokens().contains(&p.token.kind) { + // If a similar token is found, then it may be a typo. We + // consider it as a comma, and continue parsing. + err.emit(); + p.bump(); + } else { // Otherwise stop the parsing and return the error. - _ => return Err(err), + return Err(err); } } Ok(Recovered::Yes(_)) => (), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5cd02128287e..cf87c8c94954 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3115,8 +3115,7 @@ impl<'a> Parser<'a> { let arm_body; let is_fat_arrow = this.check(exp!(FatArrow)); let is_almost_fat_arrow = TokenKind::FatArrow - .similar_tokens() - .is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind)); + .similar_tokens().contains(&this.token.kind); // this avoids the compiler saying that a `,` or `}` was expected even though // the pattern isn't a never pattern (and thus an arm body is required) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 10756be6afb8..aba84d44a448 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -923,10 +923,8 @@ impl<'a> Parser<'a> { _ => { // Attempt to keep parsing if it was a similar separator. - if let Some(tokens) = exp.tok.similar_tokens() { - if tokens.contains(&self.token.kind) { - self.bump(); - } + if exp.tok.similar_tokens().contains(&self.token.kind) { + self.bump(); } } } From 5f01e12ea8035fdfc23eefaf6b580dbb9a8863ec Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 22 Jan 2025 16:50:22 +0100 Subject: [PATCH 116/342] simplify similar_tokens from Vec<_> to &[_] --- compiler/rustc_ast/src/token.rs | 12 ++++++------ compiler/rustc_parse/src/parser/expr.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 5b6e65c804f5..100f664a89fd 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -527,13 +527,13 @@ impl TokenKind { /// Returns tokens that are likely to be typed accidentally instead of the current token. /// Enables better error recovery when the wrong token is found. - pub fn similar_tokens(&self) -> Vec { + pub fn similar_tokens(&self) -> &[TokenKind] { match self { - Comma => vec![Dot, Lt, Semi], - Semi => vec![Colon, Comma], - Colon => vec![Semi], - FatArrow => vec![Eq, RArrow, Ge, Gt], - _ => vec![], + Comma => &[Dot, Lt, Semi], + Semi => &[Colon, Comma], + Colon => &[Semi], + FatArrow => &[Eq, RArrow, Ge, Gt], + _ => &[], } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index cf87c8c94954..0affca1f825c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3114,8 +3114,8 @@ impl<'a> Parser<'a> { let span_before_body = this.prev_token.span; let arm_body; let is_fat_arrow = this.check(exp!(FatArrow)); - let is_almost_fat_arrow = TokenKind::FatArrow - .similar_tokens().contains(&this.token.kind); + let is_almost_fat_arrow = + TokenKind::FatArrow.similar_tokens().contains(&this.token.kind); // this avoids the compiler saying that a `,` or `}` was expected even though // the pattern isn't a never pattern (and thus an arm body is required) From a32f64dc307192d7880212f691ff64007dee711e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 23 Jan 2025 13:41:22 +0200 Subject: [PATCH 117/342] Rephrase comment --- src/tools/rust-analyzer/lib/lsp-server/src/msg.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 074bc43388a9..2749557b91a0 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -80,9 +80,9 @@ pub struct Request { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Response { - // JSON RPC allows this to be null if it was impossible - // to decode the request's id. Ignore this special case - // and just die horribly. + // JSON-RPC allows this to be null if we can't find or parse the + // request id. We fail deserialization in that case, so we just + // make this field mandatory. pub id: RequestId, #[serde(skip_serializing_if = "Option::is_none")] pub result: Option, From 8e22ec095799a126a133a6bf6145abc39acaf591 Mon Sep 17 00:00:00 2001 From: vayunbiyani Date: Tue, 21 Jan 2025 15:06:52 -0500 Subject: [PATCH 118/342] omit unused args warnings for intrinsics without body --- compiler/rustc_passes/src/liveness.rs | 8 +++++++ tests/ui/liveness/liveness-unused.rs | 6 +++++ tests/ui/liveness/liveness-unused.stderr | 28 ++++++++++++------------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index b85a987c6411..279729365ee9 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1521,6 +1521,14 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) { + if let Some(intrinsic) = + self.ir.tcx.intrinsic(self.ir.tcx.hir().body_owner_def_id(body.id())) + { + if intrinsic.must_be_overridden { + return; + } + } + for p in body.params { self.check_unused_vars_in_pat( p.pat, diff --git a/tests/ui/liveness/liveness-unused.rs b/tests/ui/liveness/liveness-unused.rs index ba635e6638c8..49e7044aeda1 100644 --- a/tests/ui/liveness/liveness-unused.rs +++ b/tests/ui/liveness/liveness-unused.rs @@ -2,6 +2,7 @@ #![deny(unused_variables)] #![deny(unused_assignments)] #![allow(dead_code, non_camel_case_types, trivial_numeric_casts, dropping_copy_types)] +#![feature(intrinsics)] use std::ops::AddAssign; @@ -137,5 +138,10 @@ fn f7() { drop(a); } +// unused params warnings are not needed for intrinsic functions without bodies +#[rustc_intrinsic] +unsafe fn simd_shuffle(a: T, b: T, i: I) -> U; + + fn main() { } diff --git a/tests/ui/liveness/liveness-unused.stderr b/tests/ui/liveness/liveness-unused.stderr index f6c478ddbc72..a69fc10dff27 100644 --- a/tests/ui/liveness/liveness-unused.stderr +++ b/tests/ui/liveness/liveness-unused.stderr @@ -1,5 +1,5 @@ warning: unreachable statement - --> $DIR/liveness-unused.rs:92:9 + --> $DIR/liveness-unused.rs:93:9 | LL | continue; | -------- any code following this expression is unreachable @@ -14,7 +14,7 @@ LL | #![warn(unused)] = note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]` error: unused variable: `x` - --> $DIR/liveness-unused.rs:8:7 + --> $DIR/liveness-unused.rs:9:7 | LL | fn f1(x: isize) { | ^ help: if this is intentional, prefix it with an underscore: `_x` @@ -26,25 +26,25 @@ LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ error: unused variable: `x` - --> $DIR/liveness-unused.rs:12:8 + --> $DIR/liveness-unused.rs:13:8 | LL | fn f1b(x: &mut isize) { | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:20:9 + --> $DIR/liveness-unused.rs:21:9 | LL | let x: isize; | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:25:9 + --> $DIR/liveness-unused.rs:26:9 | LL | let x = 3; | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:30:13 + --> $DIR/liveness-unused.rs:31:13 | LL | let mut x = 3; | ^ @@ -52,7 +52,7 @@ LL | let mut x = 3; = note: consider using `_x` instead error: value assigned to `x` is never read - --> $DIR/liveness-unused.rs:32:5 + --> $DIR/liveness-unused.rs:33:5 | LL | x += 4; | ^ @@ -65,7 +65,7 @@ LL | #![deny(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ error: variable `z` is assigned to, but never used - --> $DIR/liveness-unused.rs:37:13 + --> $DIR/liveness-unused.rs:38:13 | LL | let mut z = 3; | ^ @@ -73,31 +73,31 @@ LL | let mut z = 3; = note: consider using `_z` instead error: unused variable: `i` - --> $DIR/liveness-unused.rs:59:12 + --> $DIR/liveness-unused.rs:60:12 | LL | Some(i) => { | ^ help: if this is intentional, prefix it with an underscore: `_i` error: unused variable: `x` - --> $DIR/liveness-unused.rs:79:9 + --> $DIR/liveness-unused.rs:80:9 | LL | for x in 1..10 { } | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:84:10 + --> $DIR/liveness-unused.rs:85:10 | LL | for (x, _) in [1, 2, 3].iter().enumerate() { } | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:89:13 + --> $DIR/liveness-unused.rs:90:13 | LL | for (_, x) in [1, 2, 3].iter().enumerate() { | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:112:9 + --> $DIR/liveness-unused.rs:113:9 | LL | let x; | ^ @@ -105,7 +105,7 @@ LL | let x; = note: consider using `_x` instead error: value assigned to `x` is never read - --> $DIR/liveness-unused.rs:116:9 + --> $DIR/liveness-unused.rs:117:9 | LL | x = 0; | ^ From 27084a25a4830b27bef9933291df4e752a6ab27c Mon Sep 17 00:00:00 2001 From: David Richey Date: Thu, 23 Jan 2025 17:36:45 -0600 Subject: [PATCH 119/342] Check cfg when collecting macro defs --- src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 1327bb3ab59c..16c7b5ca00a0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1381,6 +1381,9 @@ impl ExprCollector<'_> { } } ast::Stmt::Item(ast::Item::MacroDef(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; @@ -1390,6 +1393,9 @@ impl ExprCollector<'_> { self.collect_macro_def(statements, macro_id); } ast::Stmt::Item(ast::Item::MacroRules(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; From b06e840d9e8e27ac1f79be5e6d2778455146f665 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Fri, 24 Jan 2025 00:37:05 +0100 Subject: [PATCH 120/342] Add comments about address spaces --- compiler/rustc_codegen_llvm/src/builder.rs | 2 +- compiler/rustc_codegen_llvm/src/common.rs | 2 ++ compiler/rustc_codegen_llvm/src/consts.rs | 13 +++++++++++++ .../rustc_codegen_llvm/src/debuginfo/metadata.rs | 10 ++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 1b99fea8d515..4d572dc56fb2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1226,7 +1226,7 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { fn get_static(&mut self, def_id: DefId) -> &'ll Value { // Forward to the `get_static` method of `CodegenCx` let s = self.cx().get_static(def_id); - // Cast to default address space if statics are in a different addrspace + // Cast to default address space if globals are in a different addrspace self.cx().const_pointercast(s, self.type_ptr()) } } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index bd792c02810c..5031df06b05b 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -221,6 +221,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); } llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + // Cast to default address space if globals are in a different addrspace let g = self.const_pointercast(g, self.type_ptr()); (s.to_owned(), g) }) @@ -324,6 +325,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let llval = unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), + // Cast to the required address space if necessary self.const_pointercast(base_addr, self.type_ptr_ext(base_addr_space)), &self.const_usize(offset.bytes()), 1, diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index b88fd133e8e8..771ebf2057f9 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -214,6 +214,10 @@ impl<'ll> CodegenCx<'ll, '_> { unsafe { llvm::LLVMConstPointerCast(val, ty) } } + /// Create a global variable. + /// + /// The returned global variable is a pointer in the default address space for globals. + /// Fails if a symbol with the given name already exists. pub(crate) fn static_addr_of_mut( &self, cv: &'ll Value, @@ -237,6 +241,9 @@ impl<'ll> CodegenCx<'ll, '_> { gv } + /// Create a global constant. + /// + /// The returned global variable is a pointer in the default address space for globals. pub(crate) fn static_addr_of_impl( &self, cv: &'ll Value, @@ -534,8 +541,14 @@ impl<'ll> CodegenCx<'ll, '_> { } impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { + /// Get a pointer to a global variable. + /// + /// The pointer will always be in the default address space. If global variables default to a + /// different address space, an addrspacecast is inserted. fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value { let gv = self.static_addr_of_impl(cv, align, kind); + // static_addr_of_impl returns the bare global variable, which might not be in the default + // address space. Cast to the default address space if necessary. self.const_pointercast(gv, self.type_ptr()) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index e4da540da5cd..b9e3a6975d65 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1500,6 +1500,14 @@ fn build_vtable_type_di_node<'ll, 'tcx>( .di_node } +/// Get the global variable for the vtable. +/// +/// When using global variables, we may have created an addrspacecast to get a pointer to the +/// default address space if global variables are created in a different address space. +/// For modifying the vtable, we need the real global variable. This function accepts either a +/// global variable (which is simply returned), or an addrspacecast constant expression. +/// If the given value is an addrspacecast, the cast is removed and the global variable behind +/// the cast is returned. fn find_vtable_behind_cast<'ll>(vtable: &'ll Value) -> &'ll Value { // The vtable is a global variable, which may be behind an addrspacecast. unsafe { @@ -1532,6 +1540,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let Some(trait_ref) = trait_ref else { return }; + // Unwrap potential addrspacecast let vtable = find_vtable_behind_cast(vtable); let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty); let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); @@ -1606,6 +1615,7 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>( return; } + // Unwrap potential addrspacecast let vtable = find_vtable_behind_cast(vtable); // When full debuginfo is enabled, we want to try and prevent vtables from being From a8551362f2d9a80dbd7227947d7f68c04e3485f1 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Fri, 24 Jan 2025 09:02:00 +0900 Subject: [PATCH 121/342] set default changelog messages to fail the changelog CI --- .github/workflows/clippy_changelog.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy_changelog.yml b/.github/workflows/clippy_changelog.yml index 6df5a48c75ff..a2657bfea490 100644 --- a/.github/workflows/clippy_changelog.yml +++ b/.github/workflows/clippy_changelog.yml @@ -26,11 +26,13 @@ jobs: run: | body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || { - echo "ERROR: pull request message must contain 'changelog: ...'. Please add it." + output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") + if [ -z "$output" ]; then + echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." exit 1 - } - echo "changelog: $output" + else + echo "changelog: $output" + fi env: PYTHONIOENCODING: 'utf-8' PR_NUMBER: '${{ github.event.number }}' From 99b0ab5f52be400a770bac0e9b58e0d262d2583a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 12:56:39 +0100 Subject: [PATCH 122/342] Fix `ItemScope` not recording glob imports This caused us other code to incorrectly assume in dealing with a declaration when in fact it was dealing with a glob imported definition --- .../crates/hir-def/src/body/tests/block.rs | 2 +- .../crates/hir-def/src/import_map.rs | 1 + .../crates/hir-def/src/item_scope.rs | 206 ++++++++++-------- .../crates/hir-def/src/nameres/collector.rs | 7 +- .../crates/hir-def/src/nameres/tests.rs | 18 +- .../crates/hir-def/src/nameres/tests/globs.rs | 74 +++---- .../hir-def/src/nameres/tests/macros.rs | 26 +-- .../crates/hir-def/src/per_ns.rs | 24 +- .../crates/hir-def/src/resolver.rs | 8 +- .../rust-analyzer/crates/hir/src/symbols.rs | 64 +++--- .../crates/ide-db/src/symbol_index.rs | 22 +- 11 files changed, 239 insertions(+), 213 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index f483efa85179..e136dd18a55e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -475,7 +475,7 @@ fn outer() { block scope::tests name: _ - outer: v + outer: vg crate outer: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ac262950f13c..04c088b5a5c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -169,6 +169,7 @@ impl ImportMap { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), ImportOrExternCrate::Import(id) => Some(id.import.into()), + ImportOrExternCrate::Glob(id) => Some(id.into()), } } else { match item { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0fec7674109b..0ece878b836a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -31,10 +31,54 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { + Glob(UseId), Import(ImportId), ExternCrate(ExternCrateId), } +impl From for ImportOrExternCrate { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Glob(it) => ImportOrExternCrate::Glob(it), + ImportOrGlob::Import(it) => ImportOrExternCrate::Import(it), + } + } +} + +impl From for ImportOrExternCrate { + fn from(value: ImportType) -> Self { + match value { + ImportType::Glob(it) => ImportOrExternCrate::Glob(it), + ImportType::Import(it) => ImportOrExternCrate::Import(it), + ImportType::ExternCrate(it) => ImportOrExternCrate::ExternCrate(it), + } + } +} + +impl ImportOrExternCrate { + pub fn into_import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)), + ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)), + _ => None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrGlob { + Glob(UseId), + Import(ImportId), +} + +impl ImportOrGlob { + pub fn into_import(self) -> Option { + match self { + ImportOrGlob::Import(it) => Some(it), + _ => None, + } + } +} #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum ImportType { Import(ImportId), @@ -42,21 +86,33 @@ pub(crate) enum ImportType { ExternCrate(ExternCrateId), } -impl ImportOrExternCrate { - pub fn into_import(self) -> Option { - match self { - ImportOrExternCrate::Import(it) => Some(it), - _ => None, +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrDef { + Import(ImportId), + Glob(UseId), + ExternCrate(ExternCrateId), + Def(ModuleDefId), +} + +impl From for ImportOrDef { + fn from(value: ImportOrExternCrate) -> Self { + match value { + ImportOrExternCrate::Import(it) => ImportOrDef::Import(it), + ImportOrExternCrate::Glob(it) => ImportOrDef::Glob(it), + ImportOrExternCrate::ExternCrate(it) => ImportOrDef::ExternCrate(it), } } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ImportOrDef { - Import(ImportId), - ExternCrate(ExternCrateId), - Def(ModuleDefId), +impl From for ImportOrDef { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Import(it) => ImportOrDef::Import(it), + ImportOrGlob::Glob(it) => ImportOrDef::Glob(it), + } + } } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ImportId { pub import: UseId, @@ -96,8 +152,8 @@ pub struct ItemScope { // the resolutions of the imports of this scope use_imports_types: FxHashMap, - use_imports_values: FxHashMap, - use_imports_macros: FxHashMap, + use_imports_values: FxHashMap, + use_imports_macros: FxHashMap, use_decls: Vec, extern_crate_decls: Vec, @@ -162,7 +218,7 @@ impl ItemScope { .map(move |name| (name, self.get(name))) } - pub fn values(&self) -> impl Iterator)> + '_ { + pub fn values(&self) -> impl Iterator)> + '_ { self.values.iter().map(|(n, &i)| (n, i)) } @@ -172,7 +228,7 @@ impl ItemScope { self.types.iter().map(|(n, &i)| (n, i)) } - pub fn macros(&self) -> impl Iterator)> + '_ { + pub fn macros(&self) -> impl Iterator)> + '_ { self.macros.iter().map(|(n, &i)| (n, i)) } @@ -183,6 +239,7 @@ impl ItemScope { .filter_map(ImportOrExternCrate::into_import) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) + .filter_map(ImportOrGlob::into_import) .sorted() .dedup() } @@ -192,7 +249,7 @@ impl ItemScope { let mut def_map; let mut scope = self; - while let Some(&m) = scope.use_imports_macros.get(&import) { + while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { let module_id = i.import.lookup(db).container; @@ -224,7 +281,7 @@ impl ItemScope { } } let mut scope = self; - while let Some(&m) = scope.use_imports_values.get(&import) { + while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { let module_id = i.import.lookup(db).container; @@ -514,29 +571,11 @@ impl ItemScope { } _ => _ = glob_imports.types.remove(&lookup), } - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; + let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_types.insert( - import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_types + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -552,28 +591,12 @@ impl ItemScope { } _ => { if glob_imports.types.remove(&lookup) { - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; + let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, + prev.map_or(ImportOrDef::Def(fld.def), Into::into), ); } cov_mark::hit!(import_shadowed); @@ -597,18 +620,14 @@ impl ItemScope { _ => _ = glob_imports.values.remove(&lookup), } let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -616,19 +635,16 @@ impl ItemScope { Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { if glob_imports.values.remove(&lookup) { cov_mark::hit!(import_shadowed); + let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -649,17 +665,15 @@ impl ItemScope { _ => _ = glob_imports.macros.remove(&lookup), } let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); @@ -669,17 +683,15 @@ impl ItemScope { if glob_imports.macros.remove(&lookup) { cov_mark::hit!(import_shadowed); let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); @@ -704,16 +716,27 @@ impl ItemScope { .map(|def| &mut def.vis) .chain(self.values.values_mut().map(|def| &mut def.vis)) .chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis)) - .for_each(|vis| { - *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + .for_each(|vis| match vis { + &mut Visibility::Module(_, visibility_explicitness) => { + *vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } }); for mac in self.macros.values_mut() { if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) { continue; } - - mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit); + match mac.vis { + Visibility::Module(_, visibility_explicitness) => { + mac.vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } + } } } @@ -732,20 +755,25 @@ impl ItemScope { buf.push_str(" t"); match import { Some(ImportOrExternCrate::Import(_)) => buf.push('i'), + Some(ImportOrExternCrate::Glob(_)) => buf.push('g'), Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), None => (), } } if let Some(Item { import, .. }) = def.values { buf.push_str(" v"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if let Some(Item { import, .. }) = def.macros { buf.push_str(" m"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if def.is_none() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 1e4b42dff5fb..5058d15f3482 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -28,7 +28,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ImportType, PerNsGlobImports}, item_tree::{ self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind, @@ -527,7 +527,10 @@ impl DefCollector<'_> { // FIXME: This should specifically look for a glob import somehow and record that here self.def_map.prelude = Some(( m, - import.and_then(ImportOrExternCrate::into_import).map(|it| it.import), + import + .and_then(ImportOrExternCrate::into_import) + .and_then(ImportOrGlob::into_import) + .map(|it| it.import), )); } types => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 318aee04f7b7..73fc6787bfe8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -103,8 +103,8 @@ mod a { c: t crate::a::b::c - A: v - b: t + A: vg + b: tg "#]], ); } @@ -256,8 +256,8 @@ pub enum Foo { Bar, Baz } "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg "#]], ); } @@ -421,10 +421,10 @@ pub struct NotExported; "#, expect![[r#" crate - Exported: t v - PublicItem: t v - allowed_reexport: t - exported: t + Exported: tg vg + PublicItem: tg vg + allowed_reexport: tg + exported: tg not_allowed_reexport1: _ not_allowed_reexport2: _ "#]], @@ -692,7 +692,7 @@ mod b { b: t crate::a - T: t v + T: t vg crate::b T: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs index 8963a5767942..ddb9d4a134d3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs @@ -18,9 +18,9 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo @@ -53,20 +53,20 @@ pub use super::*; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg Foo: t v bar: t crate::foo::bar Baz: t v - Foo: t v - bar: t + Foo: tg vg + bar: tg "#]], ); } @@ -91,20 +91,20 @@ pub use super::*; ", expect![[r#" crate - Baz: t v - bar: t + Baz: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg PrivateStructFoo: t v bar: t crate::foo::bar Baz: t v PrivateStructBar: t v - PrivateStructFoo: t v - bar: t + PrivateStructFoo: tg vg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ pub(crate) struct PubCrateStruct; ", expect![[r#" crate - Foo: t - PubCrateStruct: t v - bar: t + Foo: tg + PubCrateStruct: tg vg + bar: tg foo: t crate::foo @@ -160,7 +160,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -178,7 +178,7 @@ struct Foo; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -193,8 +193,8 @@ use self::Foo::*; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -210,8 +210,8 @@ use self::Foo::{*}; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -359,7 +359,7 @@ use event::Event; event: t crate::event - Event: t v + Event: t vg serenity: t crate::event::serenity @@ -388,10 +388,10 @@ use reexport::*; "#, expect![[r#" crate - Trait: t + Trait: tg defs: t - function: v - makro: m + function: vg + makro: mg reexport: t crate::defs @@ -400,10 +400,10 @@ use reexport::*; makro: m crate::reexport - Trait: t - function: v + Trait: tg + function: vg inner: t - makro: m + makro: mg crate::reexport::inner Trait: ti @@ -442,12 +442,12 @@ mod glob_target { ShouldBePrivate: t v crate::outer - ShouldBePrivate: t v + ShouldBePrivate: tg vg inner_superglob: t crate::outer::inner_superglob - ShouldBePrivate: t v - inner_superglob: t + ShouldBePrivate: tg vg + inner_superglob: tg "#]], ); } @@ -473,20 +473,20 @@ use reexport_2::*; "#, expect![[r#" crate - Placeholder: t v + Placeholder: tg vg libs: t - reexport_1: t + reexport_1: tg reexport_2: t crate::libs Placeholder: t v crate::reexport_2 - Placeholder: t v + Placeholder: tg vg reexport_1: t crate::reexport_2::reexport_1 - Placeholder: t v + Placeholder: tg vg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index a05c4dcf9bd7..610886d55f40 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -97,9 +97,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -169,9 +169,9 @@ macro_rules! inner { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -794,7 +794,7 @@ pub trait Clone {} "#, expect![[r#" crate - Clone: t m + Clone: tg mg "#]], ); } @@ -1075,9 +1075,9 @@ macro_rules! mbe { "#, expect![[r#" crate - DummyTrait: m - attribute_macro: m - function_like_macro: m + DummyTrait: mg + attribute_macro: mg + function_like_macro: mg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 899dd4afffef..339786fc0176 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -6,7 +6,7 @@ use bitflags::bitflags; use crate::{ - item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs}, visibility::Visibility, MacroId, ModuleDefId, }; @@ -36,8 +36,8 @@ pub struct Item { } pub type TypesItem = Item; -pub type ValuesItem = Item; -pub type MacrosItem = Item; +pub type ValuesItem = Item; +pub type MacrosItem = Item; #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct PerNs { @@ -59,7 +59,7 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { + pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: Some(Item { def, vis, import }), macros: None } } @@ -84,7 +84,7 @@ impl PerNs { } } - pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { + pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) } } @@ -108,7 +108,7 @@ impl PerNs { self.values.map(|it| it.def) } - pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { + pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { self.values.map(|it| (it.def, it.import)) } @@ -116,7 +116,7 @@ impl PerNs { self.macros.map(|it| it.def) } - pub fn take_macros_import(self) -> Option<(MacroId, Option)> { + pub fn take_macros_import(self) -> Option<(MacroId, Option)> { self.macros.map(|it| (it.def, it.import)) } @@ -159,14 +159,12 @@ impl PerNs { .map(|it| (ItemInNs::Types(it.def), it.import)) .into_iter() .chain( - self.values.map(|it| { - (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.values + .map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))), ) .chain( - self.macros.map(|it| { - (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.macros + .map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))), ) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 0b9b6da8d513..8c556d8a8c3f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -19,7 +19,7 @@ use crate::{ db::DefDatabase, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, - item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, + item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE}, lang_item::LangItemTarget, nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo}, path::{ModPath, Path, PathKind}, @@ -107,7 +107,7 @@ pub enum TypeNs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResolveValueResult { - ValueNs(ValueNs, Option), + ValueNs(ValueNs, Option), Partial(TypeNs, usize, Option), } @@ -485,7 +485,7 @@ impl Resolver { db: &dyn DefDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option<(MacroId, Option)> { + ) -> Option<(MacroId, Option)> { let (item_map, module) = self.item_scope(); item_map .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) @@ -1014,7 +1014,7 @@ impl ModuleItemMap { } } -fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { +fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { let (def, import) = per_ns.take_values_import()?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a6b8ed70c363..f379a2d4d65f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -3,7 +3,7 @@ use either::Either; use hir_def::{ db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -55,9 +55,10 @@ impl DeclarationLocation { } /// Represents an outstanding module that the symbol collector must collect symbols from. +#[derive(Debug)] struct SymbolCollectorWork { module_id: ModuleId, - parent: Option, + parent: Option, } pub struct SymbolCollector<'a> { @@ -81,7 +82,15 @@ impl<'a> SymbolCollector<'a> { } } + pub fn new_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { + let mut symbol_collector = SymbolCollector::new(db); + symbol_collector.collect(module); + symbol_collector.finish() + } + pub fn collect(&mut self, module: Module) { + let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered(); + tracing::info!(?module, "SymbolCollector::collect",); self.edition = module.krate().edition(self.db); // The initial work is the root module we're collecting, additional work will @@ -97,16 +106,12 @@ impl<'a> SymbolCollector<'a> { self.symbols.into_iter().collect() } - pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { - let mut symbol_collector = SymbolCollector::new(db); - symbol_collector.collect(module); - symbol_collector.finish() - } - fn do_work(&mut self, work: SymbolCollectorWork) { + let _p = tracing::info_span!("SymbolCollector::do_work", ?work).entered(); + tracing::info!(?work, "SymbolCollector::do_work"); self.db.unwind_if_cancelled(); - let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id)); + let parent_name = work.parent.map(|name| name.as_str().to_smolstr()); self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id)); } @@ -116,18 +121,18 @@ impl<'a> SymbolCollector<'a> { ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), ModuleDefId::ConstId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::StaticId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::TraitId(id) => { this.push_decl(id, name, false); @@ -235,6 +240,7 @@ impl<'a> SymbolCollector<'a> { if is_explicit_import(vis) { match i { ImportOrExternCrate::Import(i) => push_import(self, i, name, def), + ImportOrExternCrate::Glob(_) => (), ImportOrExternCrate::ExternCrate(i) => { push_extern_crate(self, i, name, def) } @@ -249,7 +255,10 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.macros() { if let Some(i) = import { if is_explicit_import(vis) { - push_import(self, i, name, def.into()); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def.into()), + ImportOrGlob::Glob(_) => (), + } } continue; } @@ -260,7 +269,10 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.values() { if let Some(i) = import { if is_explicit_import(vis) { - push_import(self, i, name, def); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def), + ImportOrGlob::Glob(_) => (), + } } continue; } @@ -269,7 +281,7 @@ impl<'a> SymbolCollector<'a> { } for const_id in scope.unnamed_consts() { - self.collect_from_body(const_id); + self.collect_from_body(const_id, None); } for (name, id) in scope.legacy_macros() { @@ -285,7 +297,7 @@ impl<'a> SymbolCollector<'a> { } } - fn collect_from_body(&mut self, body_id: impl Into) { + fn collect_from_body(&mut self, body_id: impl Into, name: Option) { let body_id = body_id.into(); let body = self.db.body(body_id); @@ -294,7 +306,7 @@ impl<'a> SymbolCollector<'a> { for (id, _) in def_map.modules() { self.work.push(SymbolCollectorWork { module_id: def_map.module_id(id), - parent: Some(body_id), + parent: name.clone(), }); } } @@ -333,24 +345,6 @@ impl<'a> SymbolCollector<'a> { } } - fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { - match body_id { - DefWithBodyId::FunctionId(id) => { - Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::StaticId(id) => { - Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::ConstId(id) => { - Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::VariantId(id) => { - Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::InTypeConstId(_) => Some("in type const".into()), - } - } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { match assoc_item_id { AssocItemId::FunctionId(id) => self.push_decl(id, name, true), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index c94644eeb89b..e5ce10a771ef 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -143,7 +143,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module))) + Arc::new(SymbolIndex::new(SymbolCollector::new_module(db.upcast(), module))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { @@ -284,13 +284,15 @@ impl SymbolIndex { builder.insert(key, value).unwrap(); } - // FIXME: fst::Map should ideally have a way to shrink the backing buffer without the unwrap dance - let map = fst::Map::new({ - let mut buf = builder.into_inner().unwrap(); - buf.shrink_to_fit(); - buf - }) - .unwrap(); + let map = builder + .into_inner() + .and_then(|mut buf| { + fst::Map::new({ + buf.shrink_to_fit(); + buf + }) + }) + .unwrap(); SymbolIndex { symbols, map } } @@ -491,7 +493,7 @@ pub(self) use crate::Trait as IsThisJustATrait; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) @@ -518,7 +520,7 @@ struct Duplicate; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) From dfd94903c84fc267c2d771f87ab7388d00acf808 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 14:23:01 +0100 Subject: [PATCH 123/342] fix: Fix flycheck panicking with "once" invocation strategy We only ever have one flycheck runner no matter the number of workspaces, so just kick off flycheck for it immediately --- .../crates/rust-analyzer/src/flycheck.rs | 11 ++++++++ .../src/handlers/notification.rs | 25 +++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 22f06d68d80d..2309f94a7429 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -88,6 +88,17 @@ pub(crate) enum FlycheckConfig { }, } +impl FlycheckConfig { + pub(crate) fn invocation_strategy_once(&self) -> bool { + match self { + FlycheckConfig::CargoCommand { .. } => false, + FlycheckConfig::CustomCommand { invocation_strategy, .. } => { + *invocation_strategy == InvocationStrategy::Once + } + } + } +} + impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 98efc637c2c8..84ba89d9f31f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -291,9 +291,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); let may_flycheck_workspace = state.config.flycheck_workspace(None); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { + if invocation_strategy_once { + let saved_file = vfs_path.as_path().map(|p| p.to_owned()); + world.flycheck[0].restart_workspace(saved_file.clone()); + } + let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { @@ -320,16 +326,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // the user opted into package checks then let package_check_allowed = target.is_some() || !may_flycheck_workspace; if package_check_allowed { - let workspace = - world.workspaces.iter().enumerate().find(|(_, ws)| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => *cargo.workspace_root() == root, - _ => false, - }); - if let Some((idx, _)) = workspace { + let workspace = world.workspaces.iter().position(|ws| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some(idx) = workspace { world.flycheck[idx].restart_for_package(package, target); } } From 11de4b71bd8970c9b5491cc1bb13266ce7d05af8 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 24 Jan 2025 14:40:12 +0100 Subject: [PATCH 124/342] Cross-link documentation for adding a new target Both the target tier policy and the rustc-dev-guide has documentation on this, let's make sure people see both. --- src/doc/rustc-dev-guide/src/building/new-target.md | 5 +++++ src/doc/rustc/src/target-tier-policy.md | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index 1d9fa1b52d59..cd215277e69f 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -4,8 +4,13 @@ These are a set of steps to add support for a new target. There are numerous end states and paths to get there, so not all sections may be relevant to your desired goal. +See also the associated documentation in the +[target tier policy][target_tier_policy_add]. + +[target_tier_policy_add]: https://doc.rust-lang.org/rustc/target-tier-policy.html#adding-a-new-target + ## Specifying a new LLVM For very new targets, you may need to use a different fork of LLVM diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index bdcf2c0b07ef..813c052d6dbf 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -122,12 +122,16 @@ To propose addition of a new target, open a pull request on [`rust-lang/rust`]: r? compiler ``` +See also the documentation in the `rustc-dev-guide` on [adding a new target to +`rustc`][rustc_dev_guide_add_target]. + [tier3example]: https://github.com/rust-lang/rust/pull/94872 [platform_template]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/platform-support/TEMPLATE.md [summary]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/SUMMARY.md [platformsupport]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/platform-support.md [rust_compiler_team]: https://www.rust-lang.org/governance/teams/compiler [`rust-lang/rust`]: https://github.com/rust-lang/rust +[rustc_dev_guide_add_target]: https://rustc-dev-guide.rust-lang.org/building/new-target.html ## Tier 3 target policy From 024da87502fccb6756a9955e57916d6fee11b945 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 24 Jan 2025 01:53:17 +0900 Subject: [PATCH 125/342] feat: Implement `arbitrary-self-types` --- .../crates/hir-def/src/lang_item.rs | 1 + .../crates/hir-ty/src/autoderef.rs | 41 ++++++++++++---- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/infer/expr.rs | 6 +-- .../crates/hir-ty/src/method_resolution.rs | 10 ++-- .../hir-ty/src/tests/method_resolution.rs | 47 +++++++++++++++---- .../ide-completion/src/completions/dot.rs | 30 ++++++++++++ .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/test-utils/src/minicore.rs | 17 +++++++ 9 files changed, 129 insertions(+), 26 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index afdc49a2dc59..e83ce6dc42ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -372,6 +372,7 @@ language_item_table! { DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None; Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2c7076da11ab..62feca5f8cbb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -39,7 +39,7 @@ pub fn autoderef( ) -> impl Iterator { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> { at_start: bool, steps: T, explicit: bool, + use_receiver_trait: bool, } impl<'table, 'db> Autoderef<'table, 'db> { - pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self { + pub(crate) fn new( + table: &'table mut InferenceTable<'db>, + ty: Ty, + explicit: bool, + use_receiver_trait: bool, + ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit } + Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } } pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { @@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> { table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: 0, explicit } + Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } } } @@ -137,7 +144,8 @@ impl Iterator for Autoderef<'_, '_, T> { return None; } - let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?; + let (kind, new_ty) = + autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?; self.steps.push(kind, &self.ty); self.ty = new_ty; @@ -150,11 +158,12 @@ pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { - Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) + Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) } } @@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>( pub(crate) fn deref_by_trait( table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, ty: Ty, + use_receiver_trait: bool, ) -> Option { let _p = tracing::info_span!("deref_by_trait").entered(); if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { @@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait( return None; } - let deref_trait = - db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; + let trait_id = || { + if use_receiver_trait { + if let Some(receiver) = + db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait()) + { + return Some(receiver); + } + } + // Old rustc versions might not have `Receiver` trait. + // Fallback to `Deref` if they don't + db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait()) + }; + let trait_id = trait_id()?; let target = db - .trait_data(deref_trait) + .trait_data(trait_id) .associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?; let projection = { - let b = TyBuilder::subst_for_def(db, deref_trait, None); + let b = TyBuilder::subst_for_def(db, trait_id, None); if b.remaining() != 1 { // the Target type + Deref trait should only have one generic parameter, // namely Deref's Self type diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 2fe90a8a9243..d40816ba8ced 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -420,7 +420,7 @@ impl InferenceTable<'_> { let snapshot = self.snapshot(); - let mut autoderef = Autoderef::new(self, from_ty.clone(), false); + let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false); let mut first_error = None; let mut found = None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 6b6c0348dcb4..b951443897cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -487,7 +487,7 @@ impl InferenceContext<'_> { } Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { break (None, callee_ty.clone()); @@ -854,7 +854,7 @@ impl InferenceContext<'_> { if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) } else { - deref_by_trait(&mut self.table, inner_ty) + deref_by_trait(&mut self.table, inner_ty, false) .unwrap_or_else(|| self.err_ty()) } } @@ -1718,7 +1718,7 @@ impl InferenceContext<'_> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Either, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind(Interner) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 182032f04812..1cea67ee9641 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -528,7 +528,7 @@ impl ReceiverAdjustments { let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone(), true) { + match autoderef::autoderef_step(table, ty.clone(), true, false) { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); @@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver( // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( &self_ty, @@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver( ControlFlow::Continue(()) })?; table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) { // don't try to resolve methods on unknown types @@ -1709,7 +1711,7 @@ fn autoderef_method_receiver( ty: Ty, ) -> Vec<(Canonical, ReceiverAdjustments)> { let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 74acf23b75ab..8866de22dfb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1343,7 +1343,7 @@ fn foo(a: &T) { fn autoderef_visibility_field() { check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); pub struct Bar(i32); @@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() { cov_mark::check!(autoderef_candidate_not_visible); check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); impl Foo { @@ -1741,7 +1741,7 @@ fn main() { fn deref_fun_1() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1782,7 +1782,7 @@ fn test() { fn deref_fun_2() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into) { fn bad_inferred_reference_2() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver trait ExactSizeIterator { fn len(&self) -> usize; } @@ -2054,7 +2054,7 @@ fn foo() { fn box_deref_is_builtin() { check( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; #[lang = "owned_box"] @@ -2087,7 +2087,7 @@ fn test() { fn manually_drop_deref_is_not_builtin() { check( r#" -//- minicore: manually_drop, deref +//- minicore: manually_drop, receiver struct Foo; impl Foo { fn foo(&self) {} @@ -2105,7 +2105,7 @@ fn test() { fn mismatched_args_due_to_supertraits_with_deref() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; trait Trait1 { @@ -2139,3 +2139,34 @@ fn problem_method() { "#, ); } + +#[test] +fn receiver_without_deref_impl() { + check( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo1(self: &Bar) -> i32 { 42 } + fn foo2(self: Bar) -> bool { true } +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + let _v1 = bar.foo1(); + //^^^ type: i32 + let _v2 = bar.foo2(); + //^^^ type: bool +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7679d9076ded..fefea1ab13a8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1466,4 +1466,34 @@ async fn bar() { "#, ); } + + #[test] + fn receiver_without_deref_impl_completion() { + check_no_kw( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo(self: Bar) {} +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + bar.$0 +} +"#, + expect![[r#" + me foo() fn(self: Bar) +"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 7a090a6b6bb7..9bc78ff87b8a 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -394,6 +394,7 @@ define_symbols! { RangeToInclusive, Ready, receiver, + receiver_target, recursion_limit, register_attr, register_tool, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index fd06736a2524..4ed68d18e807 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -53,6 +53,7 @@ //! pin: //! pointee: copy, send, sync, ord, hash, unpin //! range: +//! receiver: deref //! result: //! send: sized //! size_of: sized @@ -513,10 +514,26 @@ pub mod ops { fn deref_mut(&mut self) -> &mut Self::Target; } // endregion:deref_mut + + // region:receiver + #[lang = "receiver"] + pub trait Receiver { + #[lang = "receiver_target"] + type Target: ?Sized; + } + + impl Receiver for P + where + P: Deref, + { + type Target = T; + } + // endregion:receiver } pub use self::deref::{ Deref, DerefMut, // :deref_mut + Receiver, // :receiver }; // endregion:deref From 1dc34eeb99c6c59de51ebe37c7680cfdc764c30d Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 24 Jan 2025 23:06:02 +0900 Subject: [PATCH 126/342] Add a new failing test that overflows stack --- .../crates/ide/src/hover/tests.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 014b751f95b0..8fe932e2df1a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -10349,3 +10349,40 @@ macro_rules! str { "#]], ); } + +#[test] +fn regression_19007() { + check( + r#" +trait Foo { + type Assoc; + + fn foo(&self) -> Self::Assoc; +} + +trait Bar { + type Target; +} + +trait Baz {} + +struct Struct { + field: T, +} + +impl Struct +where + T: Foo, + T::Assoc: Baz<::Target> + Bar, +{ + fn f(&self) { + let x$0 = self.field.foo(); + } +} + "#, + expect![ + r#" + "# + ], + ); +} From 1e5f47bf0970ae125de02a8acb5cd246da0961a8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 15:12:48 +0100 Subject: [PATCH 127/342] Record the use tree index in glob imports --- .../crates/hir-def/src/import_map.rs | 4 +- .../crates/hir-def/src/item_scope.rs | 122 +++++++++--------- .../crates/hir-def/src/nameres/collector.rs | 75 ++++++----- .../crates/hir-def/src/per_ns.rs | 2 +- .../rust-analyzer/crates/hir/src/symbols.rs | 4 +- 5 files changed, 112 insertions(+), 95 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index bb780277efca..34635997bdff 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -167,8 +167,8 @@ impl ImportMap { let attr_id = if let Some(import) = import { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), - ImportOrExternCrate::Import(id) => Some(id.import.into()), - ImportOrExternCrate::Glob(id) => Some(id.into()), + ImportOrExternCrate::Import(id) => Some(id.use_.into()), + ImportOrExternCrate::Glob(id) => Some(id.use_.into()), } } else { match item { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0ece878b836a..65a39c565611 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -31,7 +31,7 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { - Glob(UseId), + Glob(GlobId), Import(ImportId), ExternCrate(ExternCrateId), } @@ -45,29 +45,41 @@ impl From for ImportOrExternCrate { } } -impl From for ImportOrExternCrate { - fn from(value: ImportType) -> Self { - match value { - ImportType::Glob(it) => ImportOrExternCrate::Glob(it), - ImportType::Import(it) => ImportOrExternCrate::Import(it), - ImportType::ExternCrate(it) => ImportOrExternCrate::ExternCrate(it), - } - } -} - impl ImportOrExternCrate { - pub fn into_import(self) -> Option { + pub fn import_or_glob(self) -> Option { match self { ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)), ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)), _ => None, } } + + pub fn import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(it), + _ => None, + } + } + + pub fn glob(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id), + _ => None, + } + } + + pub fn use_(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id.use_), + ImportOrExternCrate::Import(id) => Some(id.use_), + _ => None, + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrGlob { - Glob(UseId), + Glob(GlobId), Import(ImportId), } @@ -79,17 +91,11 @@ impl ImportOrGlob { } } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum ImportType { - Import(ImportId), - Glob(UseId), - ExternCrate(ExternCrateId), -} #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrDef { Import(ImportId), - Glob(UseId), + Glob(GlobId), ExternCrate(ExternCrateId), Def(ModuleDefId), } @@ -115,7 +121,13 @@ impl From for ImportOrDef { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ImportId { - pub import: UseId, + pub use_: UseId, + pub idx: Idx, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct GlobId { + pub use_: UseId, pub idx: Idx, } @@ -236,7 +248,7 @@ impl ItemScope { self.use_imports_types .keys() .copied() - .filter_map(ImportOrExternCrate::into_import) + .filter_map(ImportOrExternCrate::import_or_glob) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) .filter_map(ImportOrGlob::into_import) @@ -252,7 +264,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -268,7 +280,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -284,7 +296,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -545,9 +557,13 @@ impl ItemScope { self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis) } - pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { - // FIXME: import - self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None }); + pub(crate) fn push_unnamed_trait( + &mut self, + tr: TraitId, + vis: Visibility, + import: Option, + ) { + self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import }); } pub(crate) fn push_res_with_import( @@ -555,7 +571,7 @@ impl ItemScope { glob_imports: &mut PerNsGlobImports, lookup: (LocalModuleId, Name), def: PerNs, - import: Option, + import: Option, ) -> bool { let mut changed = false; @@ -566,12 +582,11 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.types.insert(lookup.clone()); } _ => _ = glob_imports.types.remove(&lookup), } - let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types @@ -582,7 +597,7 @@ impl ItemScope { } Entry::Occupied(mut entry) => { match import { - Some(ImportType::Glob(..)) => { + Some(ImportOrExternCrate::Glob(..)) => { // Multiple globs may import the same item and they may // override visibility from previously resolved globs. This is // currently handled by `DefCollector`, because we need to @@ -591,7 +606,6 @@ impl ItemScope { } _ => { if glob_imports.types.remove(&lookup) { - let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( @@ -614,16 +628,12 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.values.insert(lookup.clone()); } _ => _ = glob_imports.values.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_values @@ -632,15 +642,13 @@ impl ItemScope { entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.values.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_values @@ -659,16 +667,12 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.macros.insert(lookup.clone()); } _ => _ = glob_imports.macros.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( @@ -679,14 +683,12 @@ impl ItemScope { entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.macros.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( @@ -856,7 +858,7 @@ impl PerNs { match def { ModuleDefId::ModuleId(_) => PerNs::types(def, v, import), ModuleDefId::FunctionId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::AdtId(adt) => match adt { AdtId::UnionId(_) => PerNs::types(def, v, import), @@ -871,14 +873,14 @@ impl PerNs { }, ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import), ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => { - PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob)) } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 5058d15f3482..06276335b718 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -28,7 +28,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ImportType, PerNsGlobImports}, + item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_tree::{ self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind, @@ -208,7 +208,7 @@ struct DefCollector<'a> { def_map: DefMap, // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap, - glob_imports: FxHashMap>, + glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec, @@ -524,14 +524,7 @@ impl DefCollector<'_> { match per_ns.types { Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => { - // FIXME: This should specifically look for a glob import somehow and record that here - self.def_map.prelude = Some(( - m, - import - .and_then(ImportOrExternCrate::into_import) - .and_then(ImportOrGlob::into_import) - .map(|it| it.import), - )); + self.def_map.prelude = Some((m, import.and_then(ImportOrExternCrate::use_))); } types => { tracing::debug!( @@ -848,13 +841,14 @@ impl DefCollector<'_> { def.values = None; def.macros = None; } - let imp = ImportType::Import(ImportId { import: id, idx: use_tree }); + let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree }); tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => { + ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => { tracing::debug!("glob import: {:?}", import); + let glob = GlobId { use_: id, idx: use_tree }; match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if is_prelude { @@ -878,7 +872,12 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } else { // glob import from same crate => we do an initial // import, and then need to propagate any further @@ -910,11 +909,16 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); // record the glob import in case we add further items - let glob = self.glob_imports.entry(m.local_id).or_default(); - match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) { - None => glob.push((module_id, vis, id)), + let glob_imports = self.glob_imports.entry(m.local_id).or_default(); + match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) { + None => glob_imports.push((module_id, vis, glob)), Some((_, old_vis, _)) => { if let Some(new_vis) = old_vis.max(vis, &self.def_map) { *old_vis = new_vis; @@ -947,7 +951,12 @@ impl DefCollector<'_> { (Some(name), res) }) .collect::>(); - self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &resolutions, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -967,7 +976,7 @@ impl DefCollector<'_> { resolutions: &[(Option, PerNs)], // Visibility this import will have vis: Visibility, - import: Option, + import: Option, ) { self.db.unwind_if_cancelled(); self.update_recursive(module_id, resolutions, vis, import, 0) @@ -981,7 +990,7 @@ impl DefCollector<'_> { // All resolutions are imported with this visibility; the visibilities in // the `PerNs` values are ignored and overwritten vis: Visibility, - import: Option, + import: Option, depth: usize, ) { if GLOB_RECURSION_LIMIT.check(depth).is_err() { @@ -997,8 +1006,10 @@ impl DefCollector<'_> { self.push_res_and_update_glob_vis(module_id, name, *res, vis, import); } None => { - let tr = match res.take_types() { - Some(ModuleDefId::TraitId(tr)) => tr, + let (tr, import) = match res.take_types_full() { + Some(Item { def: ModuleDefId::TraitId(tr), vis: _, import }) => { + (tr, import) + } Some(other) => { tracing::debug!("non-trait `_` import of {:?}", other); continue; @@ -1024,7 +1035,11 @@ impl DefCollector<'_> { if should_update { changed = true; - self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis); + self.def_map.modules[module_id].scope.push_unnamed_trait( + tr, + vis, + import.and_then(ImportOrExternCrate::import), + ); } } } @@ -1046,13 +1061,13 @@ impl DefCollector<'_> { .cloned() .collect::>(); - for (glob_importing_module, glob_import_vis, use_) in glob_imports { + for (glob_importing_module, glob_import_vis, glob) in glob_imports { let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis); self.update_recursive( glob_importing_module, resolutions, vis, - Some(ImportType::Glob(use_)), + Some(ImportOrExternCrate::Glob(glob)), depth + 1, ); } @@ -1064,7 +1079,7 @@ impl DefCollector<'_> { name: &Name, mut defs: PerNs, vis: Visibility, - def_import_type: Option, + def_import_type: Option, ) -> bool { // `extern crate crate_name` things can be re-exported as `pub use crate_name`. // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name` @@ -1077,10 +1092,10 @@ impl DefCollector<'_> { let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else { return false; }; - let Some(ImportType::Import(id)) = def_import_type else { + let Some(ImportOrExternCrate::Import(id)) = def_import_type else { return false; }; - let use_id = id.import.lookup(self.db).id; + let use_id = id.use_.lookup(self.db).id; let item_tree = use_id.item_tree(self.db); let use_kind = item_tree[use_id.value].use_tree.kind(); let UseTreeKind::Single { path, .. } = use_kind else { @@ -1103,7 +1118,7 @@ impl DefCollector<'_> { let mut changed = false; - if let Some(ImportType::Glob(_)) = def_import_type { + if let Some(ImportOrExternCrate::Glob(_)) = def_import_type { let prev_defs = self.def_map[module_id].scope.get(name); // Multiple globs may import the same item and they may override visibility from @@ -1730,7 +1745,7 @@ impl ModCollector<'_, '_> { ), )], vis, - Some(ImportType::ExternCrate(id)), + Some(ImportOrExternCrate::ExternCrate(id)), ); } else { if let Some(name) = name { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 339786fc0176..c2d3f67f17e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -78,7 +78,7 @@ impl PerNs { values: Some(Item { def: values, vis, - import: import.and_then(ImportOrExternCrate::into_import), + import: import.and_then(ImportOrExternCrate::import_or_glob), }), macros: None, } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index f379a2d4d65f..c1c45d817531 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -160,8 +160,8 @@ impl<'a> SymbolCollector<'a> { let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { let source = import_child_source_cache - .entry(i.import) - .or_insert_with(|| i.import.child_source(this.db.upcast())); + .entry(i.use_) + .or_insert_with(|| i.use_.child_source(this.db.upcast())); let Some(use_tree_src) = source.value.get(i.idx) else { return }; let Some(name_ptr) = use_tree_src .rename() From b44570fd5e8271b9126a84675f918779d5491afc Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 14:45:38 +0100 Subject: [PATCH 128/342] Lazily compute location links in type hints again --- .../crates/ide/src/inlay_hints.rs | 21 ++++++++++++------- .../ide/src/inlay_hints/closing_brace.rs | 3 ++- src/tools/rust-analyzer/crates/ide/src/lib.rs | 3 ++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 088a11bcb4cc..1f723c85df7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -650,7 +650,8 @@ struct InlayHintLabelBuilder<'a> { db: &'a RootDatabase, result: InlayHintLabel, last_part: String, - location: Option, + resolve: bool, + location: Option>, } impl fmt::Write for InlayHintLabelBuilder<'_> { @@ -663,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { never!(self.location.is_some(), "location link is already started"); self.make_new_part(); - let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; - let location = location.call_site(); - let location = - FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; - self.location = Some(location); + + self.location = Some(if self.resolve { + LazyProperty::Lazy + } else { + LazyProperty::Computed({ + let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = location.call_site(); + FileRange { file_id: location.file_id, range: location.focus_or_full_range() } + }) + }); } fn end_location_link(&mut self) { @@ -681,7 +687,7 @@ impl InlayHintLabelBuilder<'_> { if !text.is_empty() { self.result.parts.push(InlayHintLabelPart { text, - linked_location: self.location.take().map(LazyProperty::Computed), + linked_location: self.location.take(), tooltip: None, }); } @@ -753,6 +759,7 @@ fn label_of_ty( last_part: String::new(), location: None, result: InlayHintLabel::default(), + resolve: config.fields_to_resolve.resolve_label_location, }; let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition); let r = label_builder.finish(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index bd36e2c3be6f..3767d34e2c7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -12,7 +12,8 @@ use syntax::{ }; use crate::{ - inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, + InlayKind, }; pub(super) fn hints( diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 6ec95ccb62a5..41a3302c0953 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,7 +91,8 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, - InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, LazyProperty + InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty, + LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, From 0a14e17523adc4b53d372a8aef274bf1bf304562 Mon Sep 17 00:00:00 2001 From: David Richey Date: Fri, 24 Jan 2025 09:59:06 -0600 Subject: [PATCH 129/342] Explicitly add buildfiles when constructing ProjectFolders --- .../crates/load-cargo/src/lib.rs | 18 ++++++++ .../crates/project-model/src/workspace.rs | 41 +++++++++---------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 00446b27cf2f..5654c04a5928 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -256,6 +256,24 @@ impl ProjectFolders { fsc.add_file_set(file_set_roots) } + for ws in workspaces.iter() { + let mut file_set_roots: Vec = vec![]; + let mut entries = vec![]; + + for buildfile in ws.buildfiles() { + file_set_roots.push(VfsPath::from(buildfile.to_owned())); + entries.push(buildfile.to_owned()); + } + + if !file_set_roots.is_empty() { + let entry = vfs::loader::Entry::Files(entries); + res.watch.push(res.load.len()); + res.load.push(entry); + local_filesets.push(fsc.len() as u64); + fsc.add_file_set(file_set_roots) + } + } + if let Some(user_config_path) = user_config_dir_path { let ratoml_path = { let mut p = user_config_path.to_path_buf(); diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index f98d983ac060..dcd62753cb2f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -524,6 +524,17 @@ impl ProjectWorkspace { } } + pub fn buildfiles(&self) -> Vec { + match &self.kind { + ProjectWorkspaceKind::Json(project) => project + .crates() + .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone())) + .map(|build_file| self.workspace_root().join(build_file)) + .collect(), + _ => vec![], + } + } + pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { self.sysroot.discover_proc_macro_srv() } @@ -568,27 +579,15 @@ impl ProjectWorkspace { match &self.kind { ProjectWorkspaceKind::Json(project) => project .crates() - .map(|(_, krate)| { - // FIXME: PackageRoots dont allow specifying files, only directories - let build_file = krate - .build - .as_ref() - .map(|build| self.workspace_root().join(&build.build_file)) - .as_deref() - .and_then(AbsPath::parent) - .map(ToOwned::to_owned); - - PackageRoot { - is_local: krate.is_workspace_member, - include: krate - .include - .iter() - .cloned() - .chain(build_file) - .chain(self.extra_includes.iter().cloned()) - .collect(), - exclude: krate.exclude.clone(), - } + .map(|(_, krate)| PackageRoot { + is_local: krate.is_workspace_member, + include: krate + .include + .iter() + .cloned() + .chain(self.extra_includes.iter().cloned()) + .collect(), + exclude: krate.exclude.clone(), }) .collect::>() .into_iter() From 4e3e91555cdd918827bec345667a1c119034b0d6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 24 Jan 2025 16:55:29 +0000 Subject: [PATCH 130/342] Revert "Rollup merge of #135914 - compiler-errors:vanquish-query-norm, r=jackh726" This reverts commit 556d901c36511560e0ae8ce3058507121a2fb2f0, reversing changes made to be15391703babf217aaef3c854213a7fcd70e00b. --- .../src/traits/query/dropck_outlives.rs | 24 ++++++-------- compiler/rustc_traits/src/type_op.rs | 8 +++-- .../normalize-under-binder/issue-71955.rs | 8 ++--- .../normalize-under-binder/issue-71955.stderr | 32 +++++++++---------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 26ba1511b540..4004e354dc18 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -6,7 +6,8 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::traits::{ObligationCause, ObligationCtxt}; +use crate::traits::query::normalize::QueryNormalizeExt; +use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to @@ -171,18 +172,13 @@ pub fn compute_dropck_outlives_inner<'tcx>( // do not themselves define a destructor", more or less. We have // to push them onto the stack to be expanded. for ty in constraints.dtorck_types.drain(..) { - let normalized_ty = ocx.normalize(&cause, param_env, ty); + let Normalized { value: ty, obligations } = + ocx.infcx.at(&cause, param_env).query_normalize(ty)?; + ocx.register_obligations(obligations); - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}"); - return Err(NoSolution); - } + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty); - debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty); - - match normalized_ty.kind() { + match ty.kind() { // All parameters live for the duration of the // function. ty::Param(..) => {} @@ -190,12 +186,12 @@ pub fn compute_dropck_outlives_inner<'tcx>( // A projection that we couldn't resolve - it // might have a destructor. ty::Alias(..) => { - result.kinds.push(normalized_ty.into()); + result.kinds.push(ty.into()); } _ => { - if ty_set.insert(normalized_ty) { - ty_stack.push((normalized_ty, depth + 1)); + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); } } } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 5d041c2623aa..71088a598bb9 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -6,12 +6,13 @@ use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; use rustc_trait_selection::infer::InferCtxtBuilderExt; +use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{ AscribeUserType, type_op_ascribe_user_type_with_span, }; use rustc_trait_selection::traits::query::type_op::normalize::Normalize; use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { @@ -42,7 +43,10 @@ where T: fmt::Debug + TypeFoldable>, { let (param_env, Normalize { value }) = key.into_parts(); - Ok(ocx.normalize(&ObligationCause::dummy(), param_env, value)) + let Normalized { value, obligations } = + ocx.infcx.at(&ObligationCause::dummy(), param_env).query_normalize(value)?; + ocx.register_obligations(obligations); + Ok(value) } fn type_op_normalize_ty<'tcx>( diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs index 34548e2487e0..a44ed9e5ef59 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs @@ -43,9 +43,9 @@ fn main() { } foo(bar, "string", |s| s.len() == 5); - //~^ ERROR implementation of `Parser` is not general enough - //~| ERROR implementation of `Parser` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough foo(baz, "string", |s| s.0.len() == 5); - //~^ ERROR implementation of `Parser` is not general enough - //~| ERROR implementation of `Parser` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr index 23fc6e2f7f40..b2bb417a8f01 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr @@ -1,39 +1,39 @@ -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors From fff24d52ee84ad49b780443cdbfe9ca38bb85611 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 24 Jan 2025 11:25:29 -0800 Subject: [PATCH 131/342] minor: Suggest better names when a type is a sequence Previously, we'd suggest a type of `vec` for a value of type `Vec`, which is rarely what the user wants. We also had no suggestions for values of type `&[T]`. Instead, try to pluralise the inner type name, and fall back to `items`. --- .../src/handlers/extract_variable.rs | 20 ++--- .../ide-db/src/syntax_helpers/suggest_name.rs | 83 +++++++++++++++++++ 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 97321f4ec1ef..7b6f76d00452 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1672,8 +1672,8 @@ macro_rules! vec { () => {Vec} } fn main() { - let $0vec = vec![]; - let _ = vec; + let $0items = vec![]; + let _ = items; } "#, "Extract into variable", @@ -1696,8 +1696,8 @@ macro_rules! vec { () => {Vec} } fn main() { - const $0VEC: Vec = vec![]; - let _ = VEC; + const $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into constant", @@ -1720,8 +1720,8 @@ macro_rules! vec { () => {Vec} } fn main() { - static $0VEC: Vec = vec![]; - let _ = VEC; + static $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into static", @@ -2019,8 +2019,8 @@ impl Vec { } fn foo(s: &mut S) { - let $0vec = &mut s.vec; - vec.push(0); + let $0items = &mut s.vec; + items.push(0); }"#, "Extract into variable", ); @@ -2106,8 +2106,8 @@ impl Vec { } fn foo(f: &mut Y) { - let $0vec = &mut f.field.field.vec; - vec.push(0); + let $0items = &mut f.field.field.vec; + items.push(0); }"#, "Extract into variable", ); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 557c95f704b9..0a7141c19b6b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// `Result` -> `User` const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"]; +/// Generic types replaced by a plural of their first argument. +/// +/// # Examples +/// `Vec` -> "names" +const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"]; + /// Prefixes to strip from methods names /// /// # Examples @@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option Option, db: &RootDatabase, edition: Edition) -> SmolStr { + let items_str = SmolStr::new_static("items"); + let Some(inner_ty) = inner_ty else { + return items_str; + }; + let Some(name) = name_of_type(inner_ty, db, edition) else { + return items_str; + }; + + if name.ends_with(['s', 'x', 'y']) { + // Given a type called e.g. "Boss", "Fox" or "Story", don't try to + // create a plural. + items_str + } else { + SmolStr::new(format!("{name}s")) + } +} + fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option { let name = trait_.name(db).display(db, edition).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { @@ -897,6 +928,58 @@ fn foo() { $0(bar())$0; } ); } + #[test] + fn vec_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn vec_value_ends_with_s() { + check( + r#" +struct Vec {}; +struct Boss; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "items", + ); + } + + #[test] + fn vecdeque_value() { + check( + r#" +struct VecDeque {}; +struct Seed; +fn bar() -> VecDeque {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn slice_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> &[Seed] {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + #[test] fn ref_call() { check( From 2671e4a5c19492dba76905cc1e27b5dfba5fb34f Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Mon, 24 Jun 2024 09:52:09 +0800 Subject: [PATCH 132/342] add new lint `non_std_lazy_statics` detect usage of `once_cell::sync::Lazy` and `lazy_static!`, recommending usage of `std::sync::LazyLock` instead --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/non_std_lazy_statics.rs | 306 ++++++++++++++++++ clippy_utils/src/msrvs.rs | 2 +- .../auxiliary/lazy_static.rs | 20 ++ .../auxiliary/once_cell.rs | 55 ++++ .../non_std_lazy_static_fixable.fixed | 72 +++++ .../non_std_lazy_static_fixable.rs | 72 +++++ .../non_std_lazy_static_fixable.stderr | 100 ++++++ .../non_std_lazy_static_no_std.rs | 20 ++ .../non_std_lazy_static_other_once_cell.rs | 12 + .../non_std_lazy_static_unfixable.rs | 43 +++ .../non_std_lazy_static_unfixable.stderr | 66 ++++ 16 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/non_std_lazy_statics.rs create mode 100644 tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs create mode 100644 tests/ui/non_std_lazy_static/auxiliary/once_cell.rs create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs create mode 100644 tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index b87d026fda19..ce5b8fb392ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5906,6 +5906,7 @@ Released 2018-09-13 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty +[`non_std_lazy_statics`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics [`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index a3e10088db26..999df0efaac1 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -762,6 +762,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) * [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9bf6c675f000..4b0fcbfc3bc6 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -631,6 +631,7 @@ define_Conf! { mem_replace_with_default, missing_const_for_fn, needless_borrow, + non_std_lazy_statics, option_as_ref_deref, option_map_unwrap_or, ptr_as_ptr, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 6d6b415f8cdd..86410c16d95f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -576,6 +576,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::non_expressive_names::SIMILAR_NAMES_INFO, crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::non_std_lazy_statics::NON_STD_LAZY_STATICS_INFO, crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO, crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 85f99a9e0559..4b700673d0f8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -279,6 +279,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; +mod non_std_lazy_statics; mod non_zero_suggestions; mod nonstandard_macro_braces; mod octal_escapes; @@ -974,5 +975,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); + store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs new file mode 100644 index 000000000000..87466d6b61b1 --- /dev/null +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -0,0 +1,306 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{def_path_def_ids, fn_def_id, path_def_id}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Lints when `once_cell::sync::Lazy` or `lazy_static!` are used to define a static variable, + /// and suggests replacing such cases with `std::sync::LazyLock` instead. + /// + /// Note: This lint will not trigger in crate with `no_std` context, or with MSRV < 1.80.0. It + /// also will not trigger on `once_cell::sync::Lazy` usage in crates which use other types + /// from `once_cell`, such as `once_cell::race::OnceBox`. + /// + /// ### Why restrict this? + /// - Reduces the need for an extra dependency + /// - Enforce convention of using standard library types when possible + /// + /// ### Example + /// ```ignore + /// lazy_static! { + /// static ref FOO: String = "foo".to_uppercase(); + /// } + /// static BAR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| "BAR".to_lowercase()); + /// ``` + /// Use instead: + /// ```ignore + /// static FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "FOO".to_lowercase()); + /// static BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "BAR".to_lowercase()); + /// ``` + #[clippy::version = "1.81.0"] + pub NON_STD_LAZY_STATICS, + pedantic, + "lazy static that could be replaced by `std::sync::LazyLock`" +} + +/// A list containing functions with corresponding replacements in `LazyLock`. +/// +/// Some functions could be replaced as well if we have replaced `Lazy` to `LazyLock`, +/// therefore after suggesting replace the type, we need to make sure the function calls can be +/// replaced, otherwise the suggestions cannot be applied thus the applicability should be +/// `Unspecified` or `MaybeIncorret`. +static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ + ("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")), + ("once_cell::sync::Lazy::get", None), // `std::sync::LazyLock::get` is experimental + ("once_cell::sync::Lazy::new", Some("std::sync::LazyLock::new")), + // Note: `Lazy::{into_value, get_mut, force_mut}` are not in the list. + // Because the lint only checks for `static`s, and using these functions with statics + // will either be a hard error or triggers `static_mut_ref` that will be hard errors. + // But keep in mind that if somehow we decide to expand this lint to catch non-statics, + // add those functions into the list. +]; + +pub struct NonStdLazyStatic { + msrv: Msrv, + lazy_static_lazy_static: Vec, + once_cell_crate: Vec, + once_cell_sync_lazy: Vec, + once_cell_sync_lazy_new: Vec, + sugg_map: FxIndexMap>, + lazy_type_defs: FxIndexMap, + uses_other_once_cell_types: bool, +} + +impl NonStdLazyStatic { + #[must_use] + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + lazy_static_lazy_static: Vec::new(), + once_cell_crate: Vec::new(), + once_cell_sync_lazy: Vec::new(), + once_cell_sync_lazy_new: Vec::new(), + sugg_map: FxIndexMap::default(), + lazy_type_defs: FxIndexMap::default(), + uses_other_once_cell_types: false, + } + } +} + +impl_lint_pass!(NonStdLazyStatic => [NON_STD_LAZY_STATICS]); + +/// Return if current MSRV does not meet the requirement for `lazy_cell` feature, +/// or current context has `no_std` attribute. +macro_rules! ensure_prerequisite { + ($msrv:expr, $cx:ident) => { + if !$msrv.meets(clippy_utils::msrvs::LAZY_CELL) || clippy_utils::is_no_std_crate($cx) { + return; + } + }; +} + +impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { + extract_msrv_attr!(LateContext); + + fn check_crate(&mut self, cx: &LateContext<'hir>) { + // Do not lint if current crate does not support `LazyLock`. + ensure_prerequisite!(self.msrv, cx); + + // Fetch def_ids for external paths + self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); + self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); + self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); + // And CrateNums for `once_cell` crate + self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + + // Convert hardcoded fn replacement list into a map with def_id + for (path, sugg) in FUNCTION_REPLACEMENTS { + let path_vec: Vec<&str> = path.split("::").collect(); + for did in def_path_def_ids(cx.tcx, &path_vec) { + self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); + } + } + } + + fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + if let ItemKind::Static(..) = item.kind + && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) + && self.lazy_static_lazy_static.contains(¯o_call.def_id) + { + span_lint( + cx, + NON_STD_LAZY_STATICS, + macro_call.span, + "this macro has been superceded by `std::sync::LazyLock`", + ); + return; + } + + if in_external_macro(cx.sess(), item.span) { + return; + } + + if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) { + self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); + } + } + + fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &Expr<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + // All functions in the `FUNCTION_REPLACEMENTS` have only one args + if let ExprKind::Call(callee, [arg]) = expr.kind + && let Some(call_def_id) = fn_def_id(cx, expr) + && self.sugg_map.contains_key(&call_def_id) + && let ExprKind::Path(qpath) = arg.peel_borrows().kind + && let Some(arg_def_id) = cx.typeck_results().qpath_res(&qpath, arg.hir_id).opt_def_id() + && let Some(lazy_info) = self.lazy_type_defs.get_mut(&arg_def_id) + { + lazy_info.calls_span_and_id.insert(callee.span, call_def_id); + } + } + + fn check_ty(&mut self, cx: &LateContext<'hir>, ty: &'hir rustc_hir::Ty<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + // Record if types from `once_cell` besides `sync::Lazy` are used. + if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind + && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() + // Is from `once_cell` crate + && self.once_cell_crate.contains(&ty_def_id.krate) + // And is NOT `once_cell::sync::Lazy` + && !self.once_cell_sync_lazy.contains(&ty_def_id) + { + self.uses_other_once_cell_types = true; + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + if !self.uses_other_once_cell_types { + for (_, lazy_info) in &self.lazy_type_defs { + lazy_info.lint(cx, &self.sugg_map); + } + } + } +} + +struct LazyInfo { + /// Span of the [`hir::Ty`] without including args. + /// i.e.: + /// ```ignore + /// static FOO: Lazy = Lazy::new(...); + /// // ^^^^ + /// ``` + ty_span_no_args: Span, + /// `Span` and `DefId` of calls on `Lazy` type. + /// i.e.: + /// ```ignore + /// static FOO: Lazy = Lazy::new(...); + /// // ^^^^^^^^^ + /// ``` + calls_span_and_id: FxIndexMap, +} + +impl LazyInfo { + fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + // Check if item is a `once_cell:sync::Lazy` static. + if let ItemKind::Static(ty, _, body_id) = item.kind + && let Some(path_def_id) = path_def_id(cx, ty) + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && state.once_cell_sync_lazy.contains(&path_def_id) + { + let ty_span_no_args = path_span_without_args(path); + let body = cx.tcx.hir().body(body_id); + + // visit body to collect `Lazy::new` calls + let mut new_fn_calls = FxIndexMap::default(); + for_each_expr::<(), ()>(cx, body, |ex| { + if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) + && state.once_cell_sync_lazy_new.contains(&fn_did) + { + new_fn_calls.insert(call_span, fn_did); + } + std::ops::ControlFlow::Continue(()) + }); + + Some(LazyInfo { + ty_span_no_args, + calls_span_and_id: new_fn_calls, + }) + } else { + None + } + } + + fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { + // Applicability might get adjusted to `Unspecified` later if any calls + // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. + let mut appl = Applicability::MachineApplicable; + let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; + + for (span, def_id) in &self.calls_span_and_id { + let maybe_sugg = sugg_map.get(def_id).cloned().flatten(); + if let Some(sugg) = maybe_sugg { + suggs.push((*span, sugg)); + } else { + // If NO suggested replacement, not machine applicable + appl = Applicability::Unspecified; + } + } + + span_lint_and_then( + cx, + NON_STD_LAZY_STATICS, + self.ty_span_no_args, + "this type has been superceded by `LazyLock` in the standard library", + |diag| { + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + }, + ); + } +} + +/// Return the span of a given `Path` without including any of its args. +/// +/// NB: Re-write of a private function `rustc_lint::non_local_def::path_span_without_args`. +fn path_span_without_args(path: &hir::Path<'_>) -> Span { + path.segments + .last() + .and_then(|seg| seg.args) + .map_or(path.span, |args| path.span.until(args.span_ext)) +} + +/// Returns the `DefId` and `Span` of the callee if the given expression is a function call. +/// +/// NB: Modified from [`clippy_utils::fn_def_id`], to support calling in an static `Item`'s body. +fn fn_def_id_and_span_from_body(cx: &LateContext<'_>, expr: &Expr<'_>, body_id: BodyId) -> Option<(DefId, Span)> { + // FIXME: find a way to cache the result. + let typeck = cx.tcx.typeck_body(body_id); + match &expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + hir_id: path_hir_id, + span, + .. + }, + .., + ) => { + // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or + // deref to fn pointers, dyn Fn, impl Fn - #8850 + if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = + typeck.qpath_res(qpath, *path_hir_id) + { + Some((id, *span)) + } else { + None + } + }, + _ => None, + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index d9ef817306ce..d73cb7e35611 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -21,7 +21,7 @@ msrv_aliases! { 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } - 1,80,0 { BOX_INTO_ITER } + 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,74,0 { REPR_RUST } diff --git a/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs b/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs new file mode 100644 index 000000000000..85fb4e66079f --- /dev/null +++ b/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs @@ -0,0 +1,20 @@ +//! **FAKE** lazy_static crate. + +#[macro_export] +macro_rules! lazy_static { + (static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + static $N : &::core::marker::PhantomData<$T> = &::core::marker::PhantomData; + + $crate::lazy_static! { $($t)* } + }; + () => () +} + +#[macro_export] +macro_rules! external { + () => { + $crate::lazy_static! { + static ref LZ_DERP: u32 = 12; + } + }; +} diff --git a/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs b/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs new file mode 100644 index 000000000000..e860a0f7572c --- /dev/null +++ b/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs @@ -0,0 +1,55 @@ +//! **FAKE** once_cell crate. + +pub mod sync { + use std::marker::PhantomData; + + pub struct Lazy T> { + cell: PhantomData, + init: F, + } + unsafe impl Sync for Lazy {} + impl Lazy { + pub const fn new(f: F) -> Lazy { + Lazy { + cell: PhantomData, + init: f, + } + } + + pub fn into_value(this: Lazy) -> Result { + unimplemented!() + } + + pub fn force(_this: &Lazy) -> &T { + unimplemented!() + } + + pub fn force_mut(_this: &mut Lazy) -> &mut T { + unimplemented!() + } + + pub fn get(_this: &Lazy) -> Option<&T> { + unimplemented!() + } + + pub fn get_mut(_this: &mut Lazy) -> Option<&mut T> { + unimplemented!() + } + } +} +pub mod race { + pub struct OnceBox(T); + + impl OnceBox { + pub fn get(&self) -> Option<&T> { + Some(&self.0) + } + } +} + +#[macro_export] +macro_rules! external { + () => { + static OC_DERP: $crate::sync::Lazy = $crate::sync::Lazy::new(|| 12); + }; +} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed new file mode 100644 index 000000000000..f7c56b6fffe8 --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -0,0 +1,72 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + let x = "bar"; + x.to_uppercase() +}); +static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_QUX: std::sync::LazyLock = { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + if "qux".len() == 3 { + std::sync::LazyLock::new(|| "qux".to_uppercase()) + } else if "qux".is_ascii() { + std::sync::LazyLock::new(|| "qux".to_lowercase()) + } else { + std::sync::LazyLock::new(|| "qux".to_string()) + } +}; + +fn non_static() { + let _: Lazy = Lazy::new(|| 1); + let _: Lazy = Lazy::new(|| String::from("hello")); + #[allow(clippy::declare_interior_mutable_const)] + const DONT_DO_THIS: Lazy = Lazy::new(|| 1); +} + +mod once_cell_lazy_with_fns { + use once_cell::sync::Lazy; + + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_replaceable_fns() { + let _ = std::sync::LazyLock::force(&LAZY_FOO); + let _ = std::sync::LazyLock::force(&LAZY_BAR); + unsafe { + let _ = std::sync::LazyLock::force(&LAZY_BAZ); + } + } +} + +#[clippy::msrv = "1.79"] +mod msrv_not_meet { + use lazy_static::lazy_static; + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + + lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; + } +} + +mod external_macros { + once_cell::external!(); + lazy_static::external!(); +} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs new file mode 100644 index 000000000000..90bc428137ce --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -0,0 +1,72 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_BAR: Lazy = Lazy::new(|| { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + let x = "bar"; + x.to_uppercase() +}); +static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_QUX: Lazy = { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + if "qux".len() == 3 { + Lazy::new(|| "qux".to_uppercase()) + } else if "qux".is_ascii() { + Lazy::new(|| "qux".to_lowercase()) + } else { + Lazy::new(|| "qux".to_string()) + } +}; + +fn non_static() { + let _: Lazy = Lazy::new(|| 1); + let _: Lazy = Lazy::new(|| String::from("hello")); + #[allow(clippy::declare_interior_mutable_const)] + const DONT_DO_THIS: Lazy = Lazy::new(|| 1); +} + +mod once_cell_lazy_with_fns { + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_replaceable_fns() { + let _ = Lazy::force(&LAZY_FOO); + let _ = Lazy::force(&LAZY_BAR); + unsafe { + let _ = Lazy::force(&LAZY_BAZ); + } + } +} + +#[clippy::msrv = "1.79"] +mod msrv_not_meet { + use lazy_static::lazy_static; + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + + lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; + } +} + +mod external_macros { + once_cell::external!(); + lazy_static::external!(); +} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr new file mode 100644 index 000000000000..f956f4b8d52a --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr @@ -0,0 +1,100 @@ +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:11:18 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | + = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18 + | +LL | static LAZY_BAR: Lazy = Lazy::new(|| { + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18 + | +LL | static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18 + | +LL | static LAZY_QUX: Lazy = { + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_QUX: std::sync::LazyLock = { +LL | +LL | if "qux".len() == 3 { +LL ~ std::sync::LazyLock::new(|| "qux".to_uppercase()) +LL | } else if "qux".is_ascii() { +LL ~ std::sync::LazyLock::new(|| "qux".to_lowercase()) +LL | } else { +LL ~ std::sync::LazyLock::new(|| "qux".to_string()) + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:41:22 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); +LL | +... +LL | fn calling_replaceable_fns() { +LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO); + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:43:22 + | +LL | static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); +LL | +... +LL | let _ = Lazy::force(&LAZY_FOO); +LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR); + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:45:26 + | +LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); +LL | +... +LL | unsafe { +LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAZ); + | + +error: aborting due to 7 previous errors + diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs new file mode 100644 index 000000000000..6208612c6983 --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs @@ -0,0 +1,20 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![no_std] + +use lazy_static::lazy_static; +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: Lazy = Lazy::new(|| 42); +static LAZY_BAR: Lazy = Lazy::new(|| { + let x: i32 = 0; + x.saturating_add(100) +}); + +lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; +} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs new file mode 100644 index 000000000000..8701a4b7729a --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs @@ -0,0 +1,12 @@ +//@aux-build:once_cell.rs + +#![warn(clippy::non_std_lazy_statics)] + +// Should not error, since we used a type besides `sync::Lazy` +fn use_once_cell_race(x: once_cell::race::OnceBox) { + let _foo = x.get(); +} + +use once_cell::sync::Lazy; + +static LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs new file mode 100644 index 000000000000..34f8dd1ccb2e --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs @@ -0,0 +1,43 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs +//@no-rustfix + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +mod once_cell_lazy { + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_irreplaceable_fns() { + let _ = Lazy::get(&LAZY_FOO); + + unsafe { + let _ = Lazy::get_mut(&mut LAZY_BAR); + let _ = Lazy::force_mut(&mut LAZY_BAZ); + } + } +} + +mod lazy_static_lazy_static { + use lazy_static::lazy_static; + + lazy_static! { + static ref LAZY_FOO: String = "foo".to_uppercase(); + } + //~^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + lazy_static! { + static ref LAZY_BAR: String = "bar".to_uppercase(); + static ref LAZY_BAZ: String = "baz".to_uppercase(); + } + //~^^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + //~| ERROR: this macro has been superceded by `std::sync::LazyLock` +} + +fn main() {} diff --git a/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr new file mode 100644 index 000000000000..66dc435f9823 --- /dev/null +++ b/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr @@ -0,0 +1,66 @@ +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:31:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_FOO: String = "foo".to_uppercase(); +LL | | } + | |_____^ + | + = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` + +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_BAR: String = "bar".to_uppercase(); +LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); +LL | | } + | |_____^ + +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_BAR: String = "bar".to_uppercase(); +LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); +LL | | } + | |_____^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:11:22 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26 + | +LL | static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static mut LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26 + | +LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 6 previous errors + From 592eceedf50c5e11b6099a413e47dfb906feb269 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 11:12:06 +0100 Subject: [PATCH 133/342] Only collect implicit visibile use symbols if they have renames Otherwise this will pollute the index too much with unnecessary symbols --- .../rust-analyzer/crates/hir/src/symbols.rs | 87 +++++++++---------- .../test_symbol_index_collection.txt | 33 +++++++ 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index f379a2d4d65f..db6ed8b3e7f2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -158,24 +158,32 @@ impl<'a> SymbolCollector<'a> { // Nested trees are very common, so a cache here will hit a lot. let import_child_source_cache = &mut FxHashMap::default(); - let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { + let is_explicit_import = |vis| match vis { + Visibility::Public => true, + Visibility::Module(_, VisibilityExplicitness::Explicit) => true, + Visibility::Module(_, VisibilityExplicitness::Implicit) => false, + }; + + let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId, vis| { let source = import_child_source_cache .entry(i.import) .or_insert_with(|| i.import.child_source(this.db.upcast())); let Some(use_tree_src) = source.value.get(i.idx) else { return }; - let Some(name_ptr) = use_tree_src - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = use_tree_src.rename().and_then(|rename| rename.name()); + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => { + (|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))() + } + None => None, + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -188,23 +196,23 @@ impl<'a> SymbolCollector<'a> { }; let push_extern_crate = - |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| { + |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId, vis| { let loc = i.lookup(this.db.upcast()); let source = loc.source(this.db.upcast()); - let Some(name_ptr) = source - .value - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| source.value.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = source.value.rename().and_then(|rename| rename.name()); + + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => None, + None => source.value.name_ref().map(Either::Right), + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -216,18 +224,6 @@ impl<'a> SymbolCollector<'a> { }); }; - let is_explicit_import = |vis| { - match vis { - Visibility::Module(_, VisibilityExplicitness::Explicit) => true, - Visibility::Module(_, VisibilityExplicitness::Implicit) => { - // consider imports in the crate root explicit, as these are visibly - // crate-wide anyways - module_id.is_crate_root() - } - Visibility::Public => true, - } - }; - let def_map = module_id.def_map(self.db.upcast()); let scope = &def_map[module_id.local_id].scope; @@ -237,15 +233,14 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.types() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrExternCrate::Import(i) => push_import(self, i, name, def), - ImportOrExternCrate::Glob(_) => (), - ImportOrExternCrate::ExternCrate(i) => { - push_extern_crate(self, i, name, def) - } + match i { + ImportOrExternCrate::Import(i) => push_import(self, i, name, def, vis), + ImportOrExternCrate::Glob(_) => (), + ImportOrExternCrate::ExternCrate(i) => { + push_extern_crate(self, i, name, def, vis) } } + continue; } // self is a declaration @@ -254,11 +249,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.macros() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrGlob::Import(i) => push_import(self, i, name, def.into()), - ImportOrGlob::Glob(_) => (), - } + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis), + ImportOrGlob::Glob(_) => (), } continue; } @@ -268,11 +261,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.values() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrGlob::Import(i) => push_import(self, i, name, def), - ImportOrGlob::Glob(_) => (), - } + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def, vis), + ImportOrGlob::Glob(_) => (), } continue; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 535777dfcbea..7dce95592b81 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -1007,6 +1007,39 @@ is_alias: false, is_assoc: false, }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 4, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: EditionedFileId( + FileId( + 1, + ), + Edition2021, + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 85..125, + }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), + }, + container_name: None, + is_alias: false, + is_assoc: false, + }, ], ), ] From db977689fdf5f0961b0414c5518694083a763bc0 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Sat, 18 Jan 2025 18:06:04 +0900 Subject: [PATCH 134/342] trigger `obfuscated_if_else` for `.then(..).unwrap_or(..)` --- clippy_lints/src/methods/mod.rs | 8 +++---- .../src/methods/obfuscated_if_else.rs | 23 +++++++++++------- tests/ui/obfuscated_if_else.fixed | 9 +++++++ tests/ui/obfuscated_if_else.rs | 9 +++++++ tests/ui/obfuscated_if_else.stderr | 24 ++++++++++++++++--- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d7e9f65bfa3a..666e5b848ba7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2417,14 +2417,14 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `.then_some(..).unwrap_or(..)` + /// Checks for unnecessary method chains that can be simplified into `if .. else ..`. /// /// ### Why is this bad? /// This can be written more clearly with `if .. else ..` /// /// ### Limitations /// This lint currently only looks for usages of - /// `.then_some(..).unwrap_or(..)`, but will be expanded + /// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded /// to account for similar patterns. /// /// ### Example @@ -5250,8 +5250,8 @@ impl Methods { Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv); }, - Some(("then_some", t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); + Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method); }, _ => {}, } diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index 697eab32a33b..3e2cf113cb26 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,8 +1,10 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::ExprKind; use rustc_lint::LateContext; pub(super) fn check<'tcx>( @@ -11,19 +13,25 @@ pub(super) fn check<'tcx>( then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, + then_method_name: &str, ) { - // something.then_some(blah).unwrap_or(blah) - // ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr - let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { let mut applicability = Applicability::MachineApplicable; + let if_then = match then_method_name { + "then" if let ExprKind::Closure(closure) = then_arg.kind => { + let body = cx.tcx.hir().body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + _ => String::new().into(), + }; + let sugg = format!( "if {} {{ {} }} else {{ {} }}", - snippet_with_applicability(cx, then_recv.span, "..", &mut applicability), - snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability), + if_then, snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability) ); @@ -31,8 +39,7 @@ pub(super) fn check<'tcx>( cx, OBFUSCATED_IF_ELSE, expr.span, - "use of `.then_some(..).unwrap_or(..)` can be written \ - more clearly with `if .. else ..`", + "this method chain can be written more clearly with `if .. else ..`", "try", sugg, applicability, diff --git a/tests/ui/obfuscated_if_else.fixed b/tests/ui/obfuscated_if_else.fixed index c5ee569800ab..11883a8989f8 100644 --- a/tests/ui/obfuscated_if_else.fixed +++ b/tests/ui/obfuscated_if_else.fixed @@ -1,5 +1,14 @@ #![warn(clippy::obfuscated_if_else)] +#![allow(clippy::unnecessary_lazy_evaluations)] fn main() { if true { "a" } else { "b" }; + if true { "a" } else { "b" }; + + let a = 1; + if a == 1 { "a" } else { "b" }; + if a == 1 { "a" } else { "b" }; + + let partial = (a == 1).then_some("a"); + partial.unwrap_or("b"); // not lint } diff --git a/tests/ui/obfuscated_if_else.rs b/tests/ui/obfuscated_if_else.rs index 2b60c855a555..1f7896e0ffae 100644 --- a/tests/ui/obfuscated_if_else.rs +++ b/tests/ui/obfuscated_if_else.rs @@ -1,5 +1,14 @@ #![warn(clippy::obfuscated_if_else)] +#![allow(clippy::unnecessary_lazy_evaluations)] fn main() { true.then_some("a").unwrap_or("b"); + true.then(|| "a").unwrap_or("b"); + + let a = 1; + (a == 1).then_some("a").unwrap_or("b"); + (a == 1).then(|| "a").unwrap_or("b"); + + let partial = (a == 1).then_some("a"); + partial.unwrap_or("b"); // not lint } diff --git a/tests/ui/obfuscated_if_else.stderr b/tests/ui/obfuscated_if_else.stderr index d4c2f9b331a8..33985d1111bf 100644 --- a/tests/ui/obfuscated_if_else.stderr +++ b/tests/ui/obfuscated_if_else.stderr @@ -1,5 +1,5 @@ -error: use of `.then_some(..).unwrap_or(..)` can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:4:5 +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:5:5 | LL | true.then_some("a").unwrap_or("b"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` @@ -7,5 +7,23 @@ LL | true.then_some("a").unwrap_or("b"); = note: `-D clippy::obfuscated-if-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::obfuscated_if_else)]` -error: aborting due to 1 previous error +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:6:5 + | +LL | true.then(|| "a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:9:5 + | +LL | (a == 1).then_some("a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:10:5 + | +LL | (a == 1).then(|| "a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` + +error: aborting due to 4 previous errors From 0db8d05b5258f634c1c33551e104818338abd63b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 12:18:54 +0100 Subject: [PATCH 135/342] Fix flyimport not filtering via stability of import path --- .../crates/hir-def/src/find_path.rs | 25 +++++++++---- .../rust-analyzer/crates/hir-def/src/lib.rs | 3 ++ .../crates/hir-ty/src/display.rs | 1 + .../crates/ide-assists/src/assist_config.rs | 1 + .../crates/ide-completion/src/completions.rs | 2 +- .../ide-completion/src/completions/expr.rs | 4 +-- .../src/completions/flyimport.rs | 6 ++-- .../ide-completion/src/completions/postfix.rs | 2 +- .../crates/ide-completion/src/config.rs | 3 +- .../crates/ide-completion/src/context.rs | 4 ++- .../crates/ide-completion/src/lib.rs | 2 +- .../crates/ide-completion/src/render.rs | 2 +- .../crates/ide-completion/src/snippet.rs | 2 +- .../ide-completion/src/tests/flyimport.rs | 35 +++++++++++++++++++ .../crates/ide-db/src/path_transform.rs | 3 ++ .../src/handlers/json_is_not_rust.rs | 1 + .../src/handlers/missing_fields.rs | 1 + .../src/handlers/typed_hole.rs | 1 + .../crates/ide-diagnostics/src/lib.rs | 9 +++-- .../crates/ide-ssr/src/matching.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + 21 files changed, 89 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 5d67902c8ac1..c30ad0163b9d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -445,6 +445,10 @@ fn find_in_dep( }; cov_mark::hit!(partially_imported); if info.is_unstable { + if !ctx.cfg.allow_unstable { + // the item is unstable and we are not allowed to use unstable items + continue; + } choice.stability = Unstable; } @@ -670,6 +674,7 @@ mod tests { prefer_prelude: bool, prefer_absolute: bool, prefer_no_std: bool, + allow_unstable: bool, expect: Expect, ) { let (db, pos) = TestDB::with_position(ra_fixture); @@ -711,7 +716,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, + ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, @@ -732,7 +737,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, false, expect); + check_found_path_(ra_fixture, path, false, false, false, false, expect); } fn check_found_path_prelude( @@ -740,7 +745,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, true, false, false, expect); + check_found_path_(ra_fixture, path, true, false, false, false, expect); } fn check_found_path_absolute( @@ -748,7 +753,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, true, false, expect); + check_found_path_(ra_fixture, path, false, true, false, false, expect); } fn check_found_path_prefer_no_std( @@ -756,7 +761,15 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, true, expect); + check_found_path_(ra_fixture, path, false, false, true, false, expect); + } + + fn check_found_path_prefer_no_std_allow_unstable( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + path: &str, + expect: Expect, + ) { + check_found_path_(ra_fixture, path, false, false, true, true, expect); } #[test] @@ -1951,7 +1964,7 @@ pub mod ops { #[test] fn respect_unstable_modules() { - check_found_path_prefer_no_std( + check_found_path_prefer_no_std_allow_unstable( r#" //- /main.rs crate:main deps:std,core extern crate std; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index da9ffae8aab0..c78818c642ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -114,6 +114,9 @@ pub struct ImportPathConfig { pub prefer_prelude: bool, /// If true, prefer abs path (starting with `::`) where it is available. pub prefer_absolute: bool, + /// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no + /// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`. + pub allow_unstable: bool, } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 3545bf767767..d960aaf99f3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1159,6 +1159,7 @@ impl HirDisplay for Ty { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, ) { write!(f, "{}", path.display(f.db.upcast(), f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 82d8db425892..fb533077d962 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -28,6 +28,7 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable: true, } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 40669c65c576..88f893e42a6a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -660,7 +660,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index db18b531d7c3..e71017517019 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 435b88de4ae6..24243f57b46a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -316,7 +316,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -358,7 +358,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 67ea05e002b7..2c39a8fdfed7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -60,7 +60,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index c641df38ff23..45aab38e8ea0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -59,11 +59,12 @@ impl CompletionConfig<'_> { .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } - pub fn import_path_config(&self) -> ImportPathConfig { + pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig { ImportPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 3a2a4a23a198..366e79cddfa5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> { /// The module of the `scope`. pub(crate) module: hir::Module, /// Whether nightly toolchain is used. Cached since this is looked up a lot. - is_nightly: bool, + pub(crate) is_nightly: bool, + /// The edition of the current crate + // FIXME: This should probably be the crate of the current token? pub(crate) edition: Edition, /// The expected name of what we are completing. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 56d7eeaf8ea0..ac6b1207f2e0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -289,7 +289,7 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = config.import_path_config(); + let cfg = config.import_path_config(true); imports.into_iter().for_each(|(full_import_path, imported_name)| { let items_with_name = items_locator::items_with_name( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 1b7adf1adb06..dc7eacbfbafd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -301,7 +301,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 04bb178c658f..866b83a61460 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index d491e438feff..2e7c53def7fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1390,6 +1390,41 @@ pub struct FooStruct {} ); } +#[test] +fn flyimport_pattern_unstable_path() { + check( + r#" +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![""], + ); + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![[r#" + st FooStruct (use std::unstable::FooStruct) + "#]], + ); +} + #[test] fn flyimport_pattern_unstable_item_on_nightly() { check( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index a045c22c2dff..f045e44dd318 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -319,6 +319,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), @@ -378,6 +379,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; @@ -417,6 +419,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index dca889d1a8ef..f22041ebe233 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -147,6 +147,7 @@ pub(crate) fn json_in_items( prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, + allow_unstable: true, }; if !scope_has("Serialize") { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index fd1044e51bc2..938b7182bc94 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole) -> Option prefer_no_std: ctx.config.prefer_no_std, prefer_prelude: ctx.config.prefer_prelude, prefer_absolute: ctx.config.prefer_absolute, + allow_unstable: ctx.is_nightly, }, ctx.edition, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 1e99d7ad6e68..50c91a69602c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -83,7 +83,7 @@ use either::Either; use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, - base_db::SourceDatabase, + base_db::{ReleaseChannel, SourceDatabase}, generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, @@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> { sema: Semantics<'a, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, + is_nightly: bool, } impl DiagnosticsContext<'_> { @@ -368,7 +369,11 @@ pub fn semantic_diagnostics( let module = sema.file_to_module_def(file_id); - let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() }; + let is_nightly = matches!( + module.and_then(|m| db.toolchain_channel(m.krate().into())), + Some(ReleaseChannel::Nightly) | None + ); + let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly }; let mut diags = Vec::new(); match module { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 4edc3633fbe6..4bead14e31d4 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -673,6 +673,7 @@ impl Match { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index bcaec5201959..18c27c844964 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -465,6 +465,7 @@ impl flags::AnalysisStats { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, Edition::LATEST, ) From 5d94c97ddbcbe53f5a143691d3ebc0f5dd2c0e27 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 12:30:20 +0100 Subject: [PATCH 136/342] Skip redundant path search in `resolve_completion_edits` --- .../crates/ide-completion/src/item.rs | 10 +---- .../crates/ide-completion/src/lib.rs | 37 +++++-------------- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../rust-analyzer/src/handlers/request.rs | 5 +-- .../crates/rust-analyzer/src/lib.rs | 3 +- .../crates/rust-analyzer/src/lsp/ext.rs | 1 - .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +-- .../crates/syntax/src/ast/make.rs | 12 +++++- .../rust-analyzer/docs/dev/lsp-extensions.md | 2 +- 9 files changed, 27 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index b0a096b64af2..41a82409597b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -82,8 +82,7 @@ pub struct CompletionItem { pub ref_match: Option<(CompletionItemRefMode, TextSize)>, /// The import data to add to completion's edits. - /// (ImportPath, LastSegment) - pub import_to_add: SmallVec<[(String, String); 1]>, + pub import_to_add: SmallVec<[String; 1]>, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -570,12 +569,7 @@ impl Builder { let import_to_add = self .imports_to_add .into_iter() - .filter_map(|import| { - Some(( - import.import_path.display(db, self.edition).to_string(), - import.import_path.segments().last()?.display(db, self.edition).to_string(), - )) - }) + .map(|import| import.import_path.display(db, self.edition).to_string()) .collect(); CompletionItem { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index ac6b1207f2e0..8051d48ca5fe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,17 +10,13 @@ mod snippet; #[cfg(test)] mod tests; -use ide_db::text_edit::TextEdit; use ide_db::{ - helpers::mod_path_to_ast, - imports::{ - import_assets::NameToImport, - insert_use::{self, ImportScope}, - }, - items_locator, + imports::insert_use::{self, ImportScope}, syntax_helpers::tree_diff::diff, + text_edit::TextEdit, FilePosition, FxHashSet, RootDatabase, }; +use syntax::ast::make; use crate::{ completions::Completions, @@ -272,7 +268,7 @@ pub fn resolve_completion_edits( db: &RootDatabase, config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, - imports: impl IntoIterator, + imports: impl IntoIterator, ) -> Option> { let _p = tracing::info_span!("resolve_completion_edits").entered(); let sema = hir::Semantics::new(db); @@ -289,27 +285,12 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = config.import_path_config(true); - - imports.into_iter().for_each(|(full_import_path, imported_name)| { - let items_with_name = items_locator::items_with_name( - &sema, - current_crate, - NameToImport::exact_case_sensitive(imported_name), - items_locator::AssocSearchMode::Include, + imports.into_iter().for_each(|full_import_path| { + insert_use::insert_use( + &new_ast, + make::path_from_text_with_edition(&full_import_path, current_edition), + &config.insert_use, ); - let import = items_with_name - .filter_map(|candidate| { - current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg) - }) - .find(|mod_path| mod_path.display(db, current_edition).to_string() == full_import_path); - if let Some(import_path) = import { - insert_use::insert_use( - &new_ast, - mod_path_to_ast(&import_path, current_edition), - &config.insert_use, - ); - } }); diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 41a3302c0953..e942f5a6aac7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -672,7 +672,7 @@ impl Analysis { &self, config: &CompletionConfig<'_>, position: FilePosition, - imports: impl IntoIterator + std::panic::UnwindSafe, + imports: impl IntoIterator + std::panic::UnwindSafe, ) -> Cancellable> { Ok(self .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))? diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 190015d7faad..39cbf53eaa21 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1154,10 +1154,7 @@ pub(crate) fn handle_completion_resolve( .resolve_completion_edits( &forced_resolve_completions_config, position, - resolve_data - .imports - .into_iter() - .map(|import| (import.full_import_path, import.imported_name)), + resolve_data.imports.into_iter().map(|import| import.full_import_path), )? .into_iter() .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 61ec576dd4f9..ccffa7a671e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -142,9 +142,8 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; hasher.update(prefix); hasher.update(u32::from(*text_size).to_le_bytes()); } - for (import_path, import_name) in &item.import_to_add { + for import_path in &item.import_to_add { hasher.update(import_path); - hasher.update(import_name); } hasher.finalize() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 134de92feab3..ca4372aa83f8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -850,7 +850,6 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, - pub imported_name: String, } #[derive(Debug, Deserialize, Default)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 2a7d95d560d2..bff53cf98b7b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -394,10 +394,7 @@ fn completion_item( item.import_to_add .clone() .into_iter() - .map(|(import_path, import_name)| lsp_ext::CompletionImport { - full_import_path: import_path, - imported_name: import_name, - }) + .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) .collect() } else { Vec::new() diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index dca231604fa1..ff027ac5848b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -411,6 +411,11 @@ pub fn path_from_text(text: &str) -> ast::Path { ast_from_text(&format!("fn main() {{ let test: {text}; }}")) } +// FIXME: should not be pub +pub fn path_from_text_with_edition(text: &str, edition: Edition) -> ast::Path { + ast_from_text_with_edition(&format!("fn main() {{ let test: {text}; }}"), edition) +} + pub fn use_tree_glob() -> ast::UseTree { ast_from_text("use *;") } @@ -1230,7 +1235,12 @@ pub fn token_tree( #[track_caller] fn ast_from_text(text: &str) -> N { - let parse = SourceFile::parse(text, Edition::CURRENT); + ast_from_text_with_edition(text, Edition::CURRENT) +} + +#[track_caller] +fn ast_from_text_with_edition(text: &str, edition: Edition) -> N { + let parse = SourceFile::parse(text, edition); let node = match parse.tree().syntax().descendants().find_map(N::cast) { Some(it) => it, None => { diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index a632fc6f5fb8..c7ee4e402363 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ tests/ui/bytes_nth.rs:6:13 + --> tests/ui/bytes_nth.rs:7:13 | LL | let _ = s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3).copied()` @@ -8,13 +8,13 @@ LL | let _ = s.bytes().nth(3); = help: to override `-D warnings` add `#[allow(clippy::bytes_nth)]` error: called `.bytes().nth().unwrap()` on a `String` - --> tests/ui/bytes_nth.rs:7:14 + --> tests/ui/bytes_nth.rs:8:14 | LL | let _ = &s.bytes().nth(3).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.as_bytes()[3]` error: called `.bytes().nth()` on a `str` - --> tests/ui/bytes_nth.rs:8:13 + --> tests/ui/bytes_nth.rs:9:13 | LL | let _ = s[..].bytes().nth(3); | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3).copied()` diff --git a/tests/ui/slice_as_bytes.fixed b/tests/ui/slice_as_bytes.fixed new file mode 100644 index 000000000000..fcbcb80a9634 --- /dev/null +++ b/tests/ui/slice_as_bytes.fixed @@ -0,0 +1,35 @@ +//@run-rustfix +#![allow(unused)] +#![warn(clippy::slice_as_bytes)] + +use std::ops::{Index, Range}; + +struct Foo; + +struct Bar; + +impl Bar { + fn as_bytes(&self) -> &[u8] { + &[0, 1, 2, 3] + } +} + +impl Index> for Foo { + type Output = Bar; + + fn index(&self, _: Range) -> &Self::Output { + &Bar + } +} + +fn main() { + let s = "Lorem ipsum"; + let string: String = "dolor sit amet".to_owned(); + + let bytes = &s.as_bytes()[1..5]; + let bytes = &string.as_bytes()[1..]; + let bytes = &"consectetur adipiscing".as_bytes()[..=5]; + + let f = Foo; + let bytes = f[0..4].as_bytes(); +} \ No newline at end of file diff --git a/tests/ui/slice_as_bytes.rs b/tests/ui/slice_as_bytes.rs new file mode 100644 index 000000000000..4ff6c5ac7844 --- /dev/null +++ b/tests/ui/slice_as_bytes.rs @@ -0,0 +1,36 @@ +//@run-rustfix + +#![allow(unused)] +#![warn(clippy::slice_as_bytes)] + +use std::ops::{Index, Range}; + +struct Foo; + +struct Bar; + +impl Bar { + fn as_bytes(&self) -> &[u8] { + &[0, 1, 2, 3] + } +} + +impl Index> for Foo { + type Output = Bar; + + fn index(&self, _: Range) -> &Self::Output { + &Bar + } +} + +fn main() { + let s = "Lorem ipsum"; + let string: String = "dolor sit amet".to_owned(); + + let bytes = s[1..5].as_bytes(); + let bytes = string[1..].as_bytes(); + let bytes = "consectetur adipiscing"[..=5].as_bytes(); + + let f = Foo; + let bytes = f[0..4].as_bytes(); +} \ No newline at end of file diff --git a/tests/ui/slice_as_bytes.stderr b/tests/ui/slice_as_bytes.stderr new file mode 100644 index 000000000000..2a3251b4d9cb --- /dev/null +++ b/tests/ui/slice_as_bytes.stderr @@ -0,0 +1,21 @@ +error: slicing a str before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking +--> $DIR/slice_as_bytes.rs:29:17 +| +LL | let bytes = s[1..5].as_bytes(); +| ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` +| += note: `-D clippy::slice-as-bytes` implied by `-D warnings` + +error: slicing a String before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking +--> $DIR/slice_as_bytes.rs:30:17 +| +LL | let bytes = string[1..].as_bytes(); +| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]` + +error: slicing a str before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking +--> $DIR/slice_as_bytes.rs:31:17 +| +LL | let bytes = "consectetur adipiscing"[..=5].as_bytes(); +| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]` + +error: aborting due to 3 previous errors From c214f5144e1f7638dc007b0a389bb769528e56a8 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 16:07:08 +0100 Subject: [PATCH 140/342] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce5b8fb392ff..3a55c9f67ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6066,6 +6066,7 @@ Released 2018-09-13 [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next +[`slice_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#slice_as_bytes [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc From 2ad20daa2a2e9c0be30806fc93b54bf1dcca2559 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 16:13:59 +0100 Subject: [PATCH 141/342] Rollback CHANGELOG.md change --- CHANGELOG.md | 1 - tests/ui/slice_as_bytes.fixed | 2 +- tests/ui/slice_as_bytes.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a55c9f67ff2..ce5b8fb392ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6066,7 +6066,6 @@ Released 2018-09-13 [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next -[`slice_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#slice_as_bytes [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc diff --git a/tests/ui/slice_as_bytes.fixed b/tests/ui/slice_as_bytes.fixed index fcbcb80a9634..22e6ba580e20 100644 --- a/tests/ui/slice_as_bytes.fixed +++ b/tests/ui/slice_as_bytes.fixed @@ -32,4 +32,4 @@ fn main() { let f = Foo; let bytes = f[0..4].as_bytes(); -} \ No newline at end of file +} diff --git a/tests/ui/slice_as_bytes.rs b/tests/ui/slice_as_bytes.rs index 4ff6c5ac7844..ceb2ee9f66fb 100644 --- a/tests/ui/slice_as_bytes.rs +++ b/tests/ui/slice_as_bytes.rs @@ -33,4 +33,4 @@ fn main() { let f = Foo; let bytes = f[0..4].as_bytes(); -} \ No newline at end of file +} From 307d85409b141eb60724147e5cc6d41ddad569d4 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 16:37:55 +0100 Subject: [PATCH 142/342] Address linter & changelog issues --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce5b8fb392ff..3a55c9f67ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6066,6 +6066,7 @@ Released 2018-09-13 [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next +[`slice_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#slice_as_bytes [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0917dcff3085..e822aedbd2c6 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -468,6 +468,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, crate::methods::SINGLE_CHAR_ADD_STR_INFO, crate::methods::SKIP_WHILE_NEXT_INFO, + crate::methods::SLICE_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, @@ -503,7 +504,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::WAKER_CLONE_WAKE_INFO, crate::methods::WRONG_SELF_CONVENTION_INFO, crate::methods::ZST_OFFSET_INFO, - crate::methods::SLICE_AS_BYTES_INFO, crate::min_ident_chars::MIN_IDENT_CHARS_INFO, crate::minmax::MIN_MAX_INFO, crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, From 7124d822b433342360a299f3260e13b3c6b7bcf8 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 16:43:42 +0100 Subject: [PATCH 143/342] Fix function signature --- clippy_lints/src/methods/slice_as_bytes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/slice_as_bytes.rs b/clippy_lints/src/methods/slice_as_bytes.rs index 6c2aeab33080..4040008c98ac 100644 --- a/clippy_lints/src/methods/slice_as_bytes.rs +++ b/clippy_lints/src/methods/slice_as_bytes.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use super::SLICE_AS_BYTES; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { - if let ExprKind::Index(indexed, index) = recv.kind + if let ExprKind::Index(indexed, index, _) = recv.kind && is_range_literal(index) { let ty = cx.typeck_results().expr_ty(indexed).peel_refs(); From 43066fea88205824a98d207b0e38a05ce36245f7 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 17:01:56 +0100 Subject: [PATCH 144/342] Fix related test --- tests/ui/slice_as_bytes.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ui/slice_as_bytes.rs b/tests/ui/slice_as_bytes.rs index ceb2ee9f66fb..095b7773810b 100644 --- a/tests/ui/slice_as_bytes.rs +++ b/tests/ui/slice_as_bytes.rs @@ -1,5 +1,3 @@ -//@run-rustfix - #![allow(unused)] #![warn(clippy::slice_as_bytes)] From 9e83183f7ee7b7132f2bd6d857aee7bc2c7e46b3 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 17:19:21 +0100 Subject: [PATCH 145/342] uibless for tests --- tests/ui/slice_as_bytes.fixed | 1 - tests/ui/slice_as_bytes.stderr | 30 ++++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/ui/slice_as_bytes.fixed b/tests/ui/slice_as_bytes.fixed index 22e6ba580e20..da627717a529 100644 --- a/tests/ui/slice_as_bytes.fixed +++ b/tests/ui/slice_as_bytes.fixed @@ -1,4 +1,3 @@ -//@run-rustfix #![allow(unused)] #![warn(clippy::slice_as_bytes)] diff --git a/tests/ui/slice_as_bytes.stderr b/tests/ui/slice_as_bytes.stderr index 2a3251b4d9cb..9a787b0b9173 100644 --- a/tests/ui/slice_as_bytes.stderr +++ b/tests/ui/slice_as_bytes.stderr @@ -1,21 +1,23 @@ -error: slicing a str before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking ---> $DIR/slice_as_bytes.rs:29:17 -| +error: calling `as_bytes` after slicing a string + --> tests/ui/slice_as_bytes.rs:28:17 + | LL | let bytes = s[1..5].as_bytes(); -| ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` -| -= note: `-D clippy::slice-as-bytes` implied by `-D warnings` + | ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` + | + = note: `-D clippy::slice-as-bytes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::slice_as_bytes)]` -error: slicing a String before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking ---> $DIR/slice_as_bytes.rs:30:17 -| +error: calling `as_bytes` after slicing a string + --> tests/ui/slice_as_bytes.rs:29:17 + | LL | let bytes = string[1..].as_bytes(); -| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]` -error: slicing a str before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking ---> $DIR/slice_as_bytes.rs:31:17 -| +error: calling `as_bytes` after slicing a string + --> tests/ui/slice_as_bytes.rs:30:17 + | LL | let bytes = "consectetur adipiscing"[..=5].as_bytes(); -| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]` error: aborting due to 3 previous errors + From 26a7b322a37c1eeefb324bd3d082eeb36654c6a2 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 22:15:49 +0100 Subject: [PATCH 146/342] Rename slice_as_bytes -> sliced_string_as_bytes --- CHANGELOG.md | 2 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/methods/mod.rs | 10 +++++----- .../{slice_as_bytes.rs => sliced_string_as_bytes.rs} | 4 ++-- tests/ui/bytes_nth.fixed | 2 +- tests/ui/bytes_nth.rs | 2 +- ...ice_as_bytes.fixed => sliced_string_as_bytes.fixed} | 2 +- .../{slice_as_bytes.rs => sliced_string_as_bytes.rs} | 2 +- ...e_as_bytes.stderr => sliced_string_as_bytes.stderr} | 8 ++++---- 9 files changed, 17 insertions(+), 17 deletions(-) rename clippy_lints/src/methods/{slice_as_bytes.rs => sliced_string_as_bytes.rs} (93%) rename tests/ui/{slice_as_bytes.fixed => sliced_string_as_bytes.fixed} (93%) rename tests/ui/{slice_as_bytes.rs => sliced_string_as_bytes.rs} (93%) rename tests/ui/{slice_as_bytes.stderr => sliced_string_as_bytes.stderr} (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a55c9f67ff2..2757597fc511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6066,7 +6066,7 @@ Released 2018-09-13 [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next -[`slice_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#slice_as_bytes +[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e822aedbd2c6..ec223381aec6 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -468,7 +468,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, crate::methods::SINGLE_CHAR_ADD_STR_INFO, crate::methods::SKIP_WHILE_NEXT_INFO, - crate::methods::SLICE_AS_BYTES_INFO, + crate::methods::SLICED_STRING_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4a00da39a4b3..3953fe03f314 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -102,7 +102,7 @@ mod single_char_add_str; mod single_char_insert_string; mod single_char_push_string; mod skip_while_next; -mod slice_as_bytes; +mod sliced_string_as_bytes; mod stable_sort_primitive; mod str_split; mod str_splitn; @@ -4386,8 +4386,8 @@ declare_clippy_lint! { /// let s = "Lorem ipsum"; /// &s.as_bytes()[1..5]; /// ``` - #[clippy::version = "1.72.0"] - pub SLICE_AS_BYTES, + #[clippy::version = "1.86.0"] + pub SLICED_STRING_AS_BYTES, pedantic, "slicing a string and immediately calling as_bytes is less efficient and can lead to panics" } @@ -4560,7 +4560,7 @@ impl_lint_pass!(Methods => [ DOUBLE_ENDED_ITERATOR_LAST, USELESS_NONZERO_NEW_UNCHECKED, MANUAL_REPEAT_N, - SLICE_AS_BYTES, + SLICED_STRING_AS_BYTES, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4828,7 +4828,7 @@ impl Methods { if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } - slice_as_bytes::check(cx, expr, recv); + sliced_string_as_bytes::check(cx, expr, recv); }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv), diff --git a/clippy_lints/src/methods/slice_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs similarity index 93% rename from clippy_lints/src/methods/slice_as_bytes.rs rename to clippy_lints/src/methods/sliced_string_as_bytes.rs index 4040008c98ac..43aab8a452e0 100644 --- a/clippy_lints/src/methods/slice_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; use rustc_lint::LateContext; -use super::SLICE_AS_BYTES; +use super::SLICED_STRING_AS_BYTES; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind @@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { let range = snippet_with_applicability(cx, index.span, "..", &mut applicability); span_lint_and_sugg( cx, - SLICE_AS_BYTES, + SLICED_STRING_AS_BYTES, expr.span, "calling `as_bytes` after slicing a string", "try", diff --git a/tests/ui/bytes_nth.fixed b/tests/ui/bytes_nth.fixed index d58eb5227fbf..da35fcb55e5a 100644 --- a/tests/ui/bytes_nth.fixed +++ b/tests/ui/bytes_nth.fixed @@ -1,5 +1,5 @@ #![allow(clippy::unnecessary_operation)] -#![allow(clippy::slice_as_bytes)] +#![allow(clippy::sliced_string_as_bytes)] #![warn(clippy::bytes_nth)] fn main() { diff --git a/tests/ui/bytes_nth.rs b/tests/ui/bytes_nth.rs index bbfe388e8bb1..5dbe84ecec8b 100644 --- a/tests/ui/bytes_nth.rs +++ b/tests/ui/bytes_nth.rs @@ -1,5 +1,5 @@ #![allow(clippy::unnecessary_operation)] -#![allow(clippy::slice_as_bytes)] +#![allow(clippy::sliced_string_as_bytes)] #![warn(clippy::bytes_nth)] fn main() { diff --git a/tests/ui/slice_as_bytes.fixed b/tests/ui/sliced_string_as_bytes.fixed similarity index 93% rename from tests/ui/slice_as_bytes.fixed rename to tests/ui/sliced_string_as_bytes.fixed index da627717a529..469ad27a99b9 100644 --- a/tests/ui/slice_as_bytes.fixed +++ b/tests/ui/sliced_string_as_bytes.fixed @@ -1,5 +1,5 @@ #![allow(unused)] -#![warn(clippy::slice_as_bytes)] +#![warn(clippy::sliced_string_as_bytes)] use std::ops::{Index, Range}; diff --git a/tests/ui/slice_as_bytes.rs b/tests/ui/sliced_string_as_bytes.rs similarity index 93% rename from tests/ui/slice_as_bytes.rs rename to tests/ui/sliced_string_as_bytes.rs index 095b7773810b..4a4605e5a1ae 100644 --- a/tests/ui/slice_as_bytes.rs +++ b/tests/ui/sliced_string_as_bytes.rs @@ -1,5 +1,5 @@ #![allow(unused)] -#![warn(clippy::slice_as_bytes)] +#![warn(clippy::sliced_string_as_bytes)] use std::ops::{Index, Range}; diff --git a/tests/ui/slice_as_bytes.stderr b/tests/ui/sliced_string_as_bytes.stderr similarity index 75% rename from tests/ui/slice_as_bytes.stderr rename to tests/ui/sliced_string_as_bytes.stderr index 9a787b0b9173..47c928d1c1e8 100644 --- a/tests/ui/slice_as_bytes.stderr +++ b/tests/ui/sliced_string_as_bytes.stderr @@ -1,20 +1,20 @@ error: calling `as_bytes` after slicing a string - --> tests/ui/slice_as_bytes.rs:28:17 + --> tests/ui/sliced_string_as_bytes.rs:28:17 | LL | let bytes = s[1..5].as_bytes(); | ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` | = note: `-D clippy::slice-as-bytes` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::slice_as_bytes)]` + = help: to override `-D warnings` add `#[allow(clippy::sliced_string_as_bytes)]` error: calling `as_bytes` after slicing a string - --> tests/ui/slice_as_bytes.rs:29:17 + --> tests/ui/sliced_string_as_bytes.rs:29:17 | LL | let bytes = string[1..].as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]` error: calling `as_bytes` after slicing a string - --> tests/ui/slice_as_bytes.rs:30:17 + --> tests/ui/sliced_string_as_bytes.rs:30:17 | LL | let bytes = "consectetur adipiscing"[..=5].as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]` From 4fef1b46bea9a56d4170b1321787ee20e7406366 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Wed, 15 Jan 2025 22:46:18 +0100 Subject: [PATCH 147/342] Fix tests --- tests/ui/sliced_string_as_bytes.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/sliced_string_as_bytes.stderr b/tests/ui/sliced_string_as_bytes.stderr index 47c928d1c1e8..1342f4c01a48 100644 --- a/tests/ui/sliced_string_as_bytes.stderr +++ b/tests/ui/sliced_string_as_bytes.stderr @@ -4,7 +4,7 @@ error: calling `as_bytes` after slicing a string LL | let bytes = s[1..5].as_bytes(); | ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` | - = note: `-D clippy::slice-as-bytes` implied by `-D warnings` + = note: `-D clippy::sliced-string-as-bytes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::sliced_string_as_bytes)]` error: calling `as_bytes` after slicing a string From 5855e0a2f1df8a939342b16c0e7337cd393527d7 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Thu, 16 Jan 2025 01:42:41 +0100 Subject: [PATCH 148/342] Small refactoring: irrefutable let pattern --- .../src/methods/sliced_string_as_bytes.rs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/methods/sliced_string_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs index 43aab8a452e0..eb080ebe21d3 100644 --- a/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -10,21 +10,20 @@ use super::SLICED_STRING_AS_BYTES; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { if let ExprKind::Index(indexed, index, _) = recv.kind && is_range_literal(index) + && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() + && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { - let ty = cx.typeck_results().expr_ty(indexed).peel_refs(); - if ty.is_str() || is_type_lang_item(cx, ty, LangItem::String) { - let mut applicability = Applicability::MaybeIncorrect; - let stringish = snippet_with_applicability(cx, indexed.span, "..", &mut applicability); - let range = snippet_with_applicability(cx, index.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SLICED_STRING_AS_BYTES, - expr.span, - "calling `as_bytes` after slicing a string", - "try", - format!("&{stringish}.as_bytes()[{range}]"), - applicability, - ); - } + let mut applicability = Applicability::MaybeIncorrect; + let stringish = snippet_with_applicability(cx, indexed.span, "..", &mut applicability); + let range = snippet_with_applicability(cx, index.span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SLICED_STRING_AS_BYTES, + expr.span, + "calling `as_bytes` after slicing a string", + "try", + format!("&{stringish}.as_bytes()[{range}]"), + applicability, + ); } } From 4e94d222912c2a36966ae845e8fd54e073896266 Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Thu, 16 Jan 2025 16:28:59 +0100 Subject: [PATCH 149/342] nit: change placeholders --- clippy_lints/src/methods/sliced_string_as_bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/sliced_string_as_bytes.rs b/clippy_lints/src/methods/sliced_string_as_bytes.rs index eb080ebe21d3..6d4cfdb34f31 100644 --- a/clippy_lints/src/methods/sliced_string_as_bytes.rs +++ b/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -14,8 +14,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { let mut applicability = Applicability::MaybeIncorrect; - let stringish = snippet_with_applicability(cx, indexed.span, "..", &mut applicability); - let range = snippet_with_applicability(cx, index.span, "..", &mut applicability); + let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); + let range = snippet_with_applicability(cx, index.span, "_", &mut applicability); span_lint_and_sugg( cx, SLICED_STRING_AS_BYTES, From beeb6f7432a2274895dfbe4736be75bc21db827a Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Sat, 25 Jan 2025 18:38:47 +0100 Subject: [PATCH 150/342] slice-as-bytes: pedantic -> perf --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3953fe03f314..ceb21f627c97 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4388,7 +4388,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.86.0"] pub SLICED_STRING_AS_BYTES, - pedantic, + perf, "slicing a string and immediately calling as_bytes is less efficient and can lead to panics" } From 9691d2201ccb19bb8584f820c823e1525e85bc14 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 25 Jan 2025 00:54:14 +0900 Subject: [PATCH 151/342] fix: Prevent infinite recursion of bounds formatting --- .../crates/hir-ty/src/display.rs | 128 +++++++++++++----- .../crates/ide/src/hover/tests.rs | 37 ----- .../crates/ide/src/inlay_hints/bind_pat.rs | 34 +++++ 3 files changed, 128 insertions(+), 71 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 3545bf767767..8befb678c9a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -34,6 +34,7 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, Float, }; +use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::never; @@ -87,6 +88,35 @@ pub struct HirFormatter<'a> { omit_verbose_types: bool, closure_style: ClosureStyle, display_target: DisplayTarget, + bounds_formatting_ctx: BoundsFormattingCtx, +} + +#[derive(Default)] +enum BoundsFormattingCtx { + Entered { + /// We can have recursive bounds like the following case: + /// ```rust + /// where + /// T: Foo, + /// T::FooAssoc: Baz<::BarAssoc> + Bar + /// ``` + /// So, record the projection types met while formatting bounds and + //. prevent recursing into their bounds to avoid infinite loops. + projection_tys_met: FxHashSet, + }, + #[default] + Exited, +} + +impl BoundsFormattingCtx { + fn contains(&mut self, proj: &ProjectionTy) -> bool { + match self { + BoundsFormattingCtx::Entered { projection_tys_met } => { + projection_tys_met.contains(proj) + } + BoundsFormattingCtx::Exited => false, + } + } } impl HirFormatter<'_> { @@ -97,6 +127,30 @@ impl HirFormatter<'_> { fn end_location_link(&mut self) { self.fmt.end_location_link(); } + + fn format_bounds_with T>( + &mut self, + target: ProjectionTy, + format_bounds: F, + ) -> T { + match self.bounds_formatting_ctx { + BoundsFormattingCtx::Entered { ref mut projection_tys_met } => { + projection_tys_met.insert(target); + format_bounds(self) + } + BoundsFormattingCtx::Exited => { + let mut projection_tys_met = FxHashSet::default(); + projection_tys_met.insert(target); + self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met }; + let res = format_bounds(self); + // Since we want to prevent only the infinite recursions in bounds formatting + // and do not want to skip formatting of other separate bounds, clear context + // when exiting the formatting of outermost bounds + self.bounds_formatting_ctx = BoundsFormattingCtx::Exited; + res + } + } + } } pub trait HirDisplay { @@ -220,6 +274,7 @@ pub trait HirDisplay { closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::SourceCode { module_id, allow_opaque }, show_container_bounds: false, + bounds_formatting_ctx: Default::default(), }) { Ok(()) => {} Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), @@ -427,6 +482,7 @@ impl HirDisplayWrapper<'_, T> { display_target: self.display_target, closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, + bounds_formatting_ctx: Default::default(), }) } @@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy { // `::Assoc` if !f.display_target.is_source_code() { if let TyKind::Placeholder(idx) = self_ty.kind(Interner) { - let db = f.db; - let id = from_placeholder_idx(db, *idx); - let generics = generics(db.upcast(), id.parent); + if !f.bounds_formatting_ctx.contains(self) { + let db = f.db; + let id = from_placeholder_idx(db, *idx); + let generics = generics(db.upcast(), id.parent); - let substs = generics.placeholder_subst(db); - let bounds = db - .generic_predicates(id.parent) - .iter() - .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match wc.skip_binders() { - WhereClause::Implemented(tr) => { - match tr.self_type_parameter(Interner).kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, + let substs = generics.placeholder_subst(db); + let bounds = db + .generic_predicates(id.parent) + .iter() + .map(|pred| pred.clone().substitute(Interner, &substs)) + .filter(|wc| match wc.skip_binders() { + WhereClause::Implemented(tr) => { + matches!( + tr.self_type_parameter(Interner).kind(Interner), + TyKind::Alias(_) + ) } - } - WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, - }, - // We shouldn't be here if these exist - WhereClause::AliasEq(_) => false, - WhereClause::LifetimeOutlives(_) => false, - }) - .collect::>(); - if !bounds.is_empty() { - return write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left( - &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), - ), - &bounds, - SizedByDefault::NotSized, - ); - }; + WhereClause::TypeOutlives(t) => { + matches!(t.ty.kind(Interner), TyKind::Alias(_)) + } + // We shouldn't be here if these exist + WhereClause::AliasEq(_) => false, + WhereClause::LifetimeOutlives(_) => false, + }) + .collect::>(); + if !bounds.is_empty() { + return f.format_bounds_with(self.clone(), |f| { + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left( + &TyKind::Alias(AliasTy::Projection(self.clone())) + .intern(Interner), + ), + &bounds, + SizedByDefault::NotSized, + ) + }); + } + } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 8fe932e2df1a..014b751f95b0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -10349,40 +10349,3 @@ macro_rules! str { "#]], ); } - -#[test] -fn regression_19007() { - check( - r#" -trait Foo { - type Assoc; - - fn foo(&self) -> Self::Assoc; -} - -trait Bar { - type Target; -} - -trait Baz {} - -struct Struct { - field: T, -} - -impl Struct -where - T: Foo, - T::Assoc: Baz<::Target> + Bar, -{ - fn f(&self) { - let x$0 = self.field.foo(); - } -} - "#, - expect![ - r#" - "# - ], - ); -} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index ab5464156f0a..01a1a4545c47 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1203,6 +1203,40 @@ fn f5>(it: G) { let l = it.f(); //^ () } +"#, + ); + } + + #[test] + fn regression_19007() { + check_types( + r#" +trait Foo { + type Assoc; + + fn foo(&self) -> Self::Assoc; +} + +trait Bar { + type Target; +} + +trait Baz {} + +struct Struct { + field: T, +} + +impl Struct +where + T: Foo, + T::Assoc: Baz<::Target> + Bar, +{ + fn f(&self) { + let x = self.field.foo(); + //^ impl Baz<<::Assoc as Bar>::Target> + Bar + } +} "#, ); } From 6e1690a504081d8dfe9de1dad60823c9ce265d18 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 25 Jan 2025 20:53:34 +0000 Subject: [PATCH 152/342] Pass spans to perform_locally_in_new_solver --- .../src/diagnostics/bound_region_errors.rs | 2 +- .../src/traits/query/dropck_outlives.rs | 3 ++- .../traits/query/type_op/ascribe_user_type.rs | 6 +++--- .../query/type_op/implied_outlives_bounds.rs | 17 ++++++++++------- .../src/traits/query/type_op/mod.rs | 3 ++- .../src/traits/query/type_op/normalize.rs | 4 +++- .../src/traits/query/type_op/outlives.rs | 4 +++- .../src/traits/query/type_op/prove_predicate.rs | 4 +++- compiler/rustc_traits/src/dropck_outlives.rs | 3 ++- .../rustc_traits/src/implied_outlives_bounds.rs | 5 +++-- compiler/rustc_traits/src/type_op.rs | 3 ++- 11 files changed, 34 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 90d12ea83285..475897d8f3e3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -310,7 +310,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { let (infcx, key, _) = mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); - type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?; + type_op_ascribe_user_type_with_span(&ocx, key, cause.span).ok()?; let diag = try_extract_error_from_fulfill_cx( &ocx, mbcx.mir_def_id(), diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 26ba1511b540..90919d3889ea 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -90,6 +90,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn compute_dropck_outlives_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>, + span: Span, ) -> Result, NoSolution> { let tcx = ocx.infcx.tcx; let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal; @@ -135,7 +136,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); - let cause = ObligationCause::dummy(); + let cause = ObligationCause::dummy_with_span(span); let mut constraints = DropckConstraint::empty(); while let Some((ty, depth)) = ty_stack.pop() { debug!( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 254dee794f1b..4eecde00eaa1 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -30,8 +30,9 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, span) } } @@ -41,11 +42,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { pub fn type_op_ascribe_user_type_with_span<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, - span: Option, + span: Span, ) -> Result<(), NoSolution> { let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); - let span = span.unwrap_or(DUMMY_SP); match user_ty.kind { UserTypeKind::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, UserTypeKind::TypeOf(def_id, user_args) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index fe47e837dfb5..1339739ce7f5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -5,7 +5,7 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; -use rustc_span::DUMMY_SP; +use rustc_span::Span; use rustc_span::def_id::CRATE_DEF_ID; use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::{SmallVec, smallvec}; @@ -45,11 +45,12 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span) } else { - compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty, span) } } } @@ -58,13 +59,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result>, NoSolution> { let normalize_op = |ty| -> Result<_, NoSolution> { // We must normalize the type so we can compute the right outlives components. // for example, if we have some constrained param type like `T: Trait`, // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. let ty = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) .map_err(|_| NoSolution)?; if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); @@ -142,6 +144,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result>, NoSolution> { let tcx = ocx.infcx.tcx; @@ -171,8 +174,8 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // FIXME(@lcnr): It's not really "always fine", having fewer implied // bounds can be backward incompatible, e.g. #101951 was caused by // us not dealing with inference vars in `TypeOutlives` predicates. - let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) - .unwrap_or_default(); + let obligations = + wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, span).unwrap_or_default(); for obligation in obligations { debug!(?obligation); @@ -255,7 +258,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // Need to manually normalize in the new solver as `wf::obligations` does not. if ocx.infcx.next_trait_solver() { ty_a = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty_a) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty_a) .map_err(|_| NoSolution)?; } let mut components = smallvec![]; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 54fce914bb65..68feb19c55b8 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -92,6 +92,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result; fn fully_perform_into( @@ -152,7 +153,7 @@ where if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, - |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self), + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), "query type op", span, )? diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 94df222932ef..e8c2528aa6ee 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -5,6 +5,7 @@ use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::Normalize; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -29,9 +30,10 @@ where fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { // FIXME(-Znext-solver): shouldn't be using old normalizer - Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) + Ok(ocx.normalize(&ObligationCause::dummy_with_span(span), key.param_env, key.value.value)) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index fa05f901f663..99a2779aa821 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,5 +1,6 @@ use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -28,7 +29,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value)) + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value), span) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index b2dab379262f..4f9e2e79d624 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -57,10 +58,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { ocx.register_obligation(Obligation::new( ocx.infcx.tcx, - ObligationCause::dummy(), + ObligationCause::dummy_with_span(span), key.param_env, key.value.predicate, )); diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 51e4dbe81b3d..b3377e15aa79 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -6,6 +6,7 @@ use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::{self, GenericArgs, TyCtxt}; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, dtorck_constraint_for_ty_inner, @@ -24,7 +25,7 @@ fn dropck_outlives<'tcx>( debug!("dropck_outlives(goal={:#?})", canonical_goal); tcx.infer_ctxt().enter_canonical_trait_query(&canonical_goal, |ocx, goal| { - compute_dropck_outlives_inner(ocx, goal) + compute_dropck_outlives_inner(ocx, goal, DUMMY_SP) }) } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index a51eefd908cc..5f75e242a50f 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -8,6 +8,7 @@ use rustc_infer::traits::query::OutlivesBound; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, @@ -28,7 +29,7 @@ fn implied_outlives_bounds_compat<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty, DUMMY_SP) }) } @@ -41,6 +42,6 @@ fn implied_outlives_bounds<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_inner(ocx, param_env, ty, DUMMY_SP) }) } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 5d041c2623aa..506f1a7a1c6e 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -5,6 +5,7 @@ use rustc_infer::infer::canonical::{Canonical, CanonicalQueryInput, QueryRespons use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{ AscribeUserType, type_op_ascribe_user_type_with_span, @@ -30,7 +31,7 @@ fn type_op_ascribe_user_type<'tcx>( canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, DUMMY_SP) }) } From 9ae0e7bb25e60a0d387729174ac9e64d4ede5938 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 26 Jan 2025 00:13:11 +0200 Subject: [PATCH 153/342] Don't complete doc(hidden) enum variants and use trees Also refactor the check a bit. --- .../crates/ide-completion/src/completions.rs | 88 ++++++++----------- .../src/completions/item_list/trait_impl.rs | 4 +- .../ide-completion/src/completions/use_.rs | 10 ++- .../crates/ide-completion/src/context.rs | 17 +++- .../ide-completion/src/tests/expression.rs | 21 +++++ .../ide-completion/src/tests/use_tree.rs | 17 ++++ 6 files changed, 102 insertions(+), 55 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 88f893e42a6a..a22e7b272ea0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -188,9 +188,6 @@ impl Completions { resolution: hir::ScopeDef, doc_aliases: Vec, ) { - if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { - return; - } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -216,9 +213,6 @@ impl Completions { local_name: hir::Name, resolution: hir::ScopeDef, ) { - if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { - return; - } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -241,7 +235,7 @@ impl Completions { path_ctx: &PathCompletionCtx, e: hir::Enum, ) { - if !ctx.check_stability(Some(&e.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(e) { return; } e.variants(ctx.db) @@ -257,9 +251,6 @@ impl Completions { local_name: hir::Name, doc_aliases: Vec, ) { - if !ctx.check_stability(Some(&module.attrs(ctx.db))) { - return; - } self.add_path_resolution( ctx, path_ctx, @@ -276,9 +267,6 @@ impl Completions { mac: hir::Macro, local_name: hir::Name, ) { - if !ctx.check_stability(Some(&mac.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&mac) { Visible::Yes => false, Visible::Editable => true, @@ -302,9 +290,6 @@ impl Completions { func: hir::Function, local_name: Option, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -332,9 +317,6 @@ impl Completions { receiver: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -362,9 +344,6 @@ impl Completions { func: hir::Function, import: LocatedImport, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -387,9 +366,6 @@ impl Completions { } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { - if !ctx.check_stability(Some(&konst.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -406,9 +382,6 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { - if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&type_alias) { Visible::Yes => false, Visible::Editable => true, @@ -438,7 +411,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } if let Some(builder) = @@ -455,7 +428,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { @@ -479,9 +452,6 @@ impl Completions { field: hir::Field, ty: &hir::Type, ) { - if !ctx.check_stability(Some(&field.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&field) { Visible::Yes => false, Visible::Editable => true, @@ -506,12 +476,18 @@ impl Completions { path: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { - return; - } - if let Some(builder) = - render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) - { + let is_private_editable = match ctx.is_visible(&strukt) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + if let Some(builder) = render_struct_literal( + RenderContext::new(ctx).private_editable(is_private_editable), + path_ctx, + strukt, + path, + local_name, + ) { self.add(builder.build(ctx.db)); } } @@ -523,10 +499,17 @@ impl Completions { path: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&un.attrs(ctx.db))) { - return; - } - let item = render_union_literal(RenderContext::new(ctx), un, path, local_name); + let is_private_editable = match ctx.is_visible(&un) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + let item = render_union_literal( + RenderContext::new(ctx).private_editable(is_private_editable), + un, + path, + local_name, + ); self.add_opt(item); } @@ -571,7 +554,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } self.add_opt(render_variant_pat( @@ -591,7 +574,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } let path = Some(&path); @@ -612,10 +595,17 @@ impl Completions { strukt: hir::Struct, local_name: Option, ) { - if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { - return; - } - self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); + let is_private_editable = match ctx.is_visible(&strukt) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + self.add_opt(render_struct_pat( + RenderContext::new(ctx).private_editable(is_private_editable), + pattern_ctx, + strukt, + local_name, + )); } pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 5f0288ae9509..831f5665f4aa 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,7 +31,7 @@ //! } //! ``` -use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name}; +use hir::{db::ExpandDatabase, MacroFileId, Name}; use ide_db::text_edit::TextEdit; use ide_db::{ documentation::HasDocs, path_transform::PathTransform, @@ -155,7 +155,7 @@ fn complete_trait_impl( if let Some(hir_impl) = ctx.sema.to_def(impl_def) { get_missing_assoc_items(&ctx.sema, impl_def) .into_iter() - .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db)))) + .filter(|item| ctx.check_stability_and_hidden(*item)) .for_each(|item| { use self::ImplCompletionKind::*; match (item, kind) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 9d62622add20..b384987c51ce 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -52,8 +52,14 @@ pub(crate) fn complete_use_path( ) }; for (name, def) in module_scope { - if !ctx.check_stability(def.attrs(ctx.db).as_deref()) { - continue; + if let (Some(attrs), Some(defining_crate)) = + (def.attrs(ctx.db), def.krate(ctx.db)) + { + if !ctx.check_stability(Some(&attrs)) + || ctx.is_doc_hidden(&attrs, defining_crate) + { + continue; + } } let is_name_already_imported = already_imported_names.contains(name.as_str()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 366e79cddfa5..2f1860cbb59a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -534,7 +534,7 @@ impl CompletionContext<'_> { } } - /// Checks if an item is visible and not `doc(hidden)` at the completion site. + /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site. pub(crate) fn is_visible(&self, item: &I) -> Visible where I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy, @@ -570,6 +570,15 @@ impl CompletionContext<'_> { !attrs.is_unstable() || self.is_nightly } + pub(crate) fn check_stability_and_hidden(&self, item: I) -> bool + where + I: hir::HasAttrs + hir::HasCrate, + { + let defining_crate = item.krate(self.db); + let attrs = item.attrs(self.db); + self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate) + } + /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { @@ -647,6 +656,10 @@ impl CompletionContext<'_> { attrs: &hir::Attrs, defining_crate: hir::Crate, ) -> Visible { + if !self.check_stability(Some(attrs)) { + return Visible::No; + } + if !vis.is_visible_from(self.db, self.module.into()) { if !self.config.enable_private_editable { return Visible::No; @@ -666,7 +679,7 @@ impl CompletionContext<'_> { } } - fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { + pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { // `doc(hidden)` items are only completed within the defining crate. self.krate != defining_crate && attrs.has_doc_hidden() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index e117dbf4bdf0..663a038580d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1965,3 +1965,24 @@ fn bar() { "#]], ); } + +#[test] +fn doc_hidden_enum_variant() { + check( + r#" +//- /foo.rs crate:foo +pub enum Enum { + #[doc(hidden)] Hidden, + Visible, +} + +//- /lib.rs crate:lib deps:foo +fn foo() { + let _ = foo::Enum::$0; +} + "#, + expect![[r#" + ev Visible Visible + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs index 04b3a47a64da..593b1edde5cc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs @@ -451,3 +451,20 @@ marco_rules! m { () => {} } "#]], ); } + +#[test] +fn use_tree_doc_hidden() { + check( + r#" +//- /foo.rs crate:foo +#[doc(hidden)] pub struct Hidden; +pub struct Visible; + +//- /lib.rs crate:lib deps:foo +use foo::$0; + "#, + expect![[r#" + st Visible Visible + "#]], + ); +} From 2117afdef8a3dfd82c6a3dd4d974a2213c2aeb1f Mon Sep 17 00:00:00 2001 From: wowinter13 Date: Sat, 25 Jan 2025 23:18:18 +0100 Subject: [PATCH 154/342] [Clippy] Add vec_reserve & vecdeque_reserve diagnostic items --- compiler/rustc_span/src/symbol.rs | 2 ++ library/alloc/src/collections/vec_deque/mod.rs | 1 + library/alloc/src/vec/mod.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index aa22d0f706dd..43e47127ba26 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2186,8 +2186,10 @@ symbols! { vec_macro, vec_new, vec_pop, + vec_reserve, vec_with_capacity, vecdeque_iter, + vecdeque_reserve, vector, version, vfp2, diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 1c33f8f60d82..299c8b8679e3 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -823,6 +823,7 @@ impl VecDeque { /// assert!(buf.capacity() >= 11); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_reserve")] #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 54673ceb1da8..48afcf6e0645 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1267,6 +1267,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } From 2640f657ec8bd64276f85741d268e7a87e35de6e Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Wed, 22 Jan 2025 18:57:20 +0900 Subject: [PATCH 155/342] change the applicability of `obfuscated_if_else` depending on whether the original code can have side effects --- clippy_lints/src/methods/obfuscated_if_else.rs | 8 +++++++- tests/ui/obfuscated_if_else.fixed | 6 +++++- tests/ui/obfuscated_if_else.rs | 6 +++++- tests/ui/obfuscated_if_else.stderr | 14 +++++++++++++- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/obfuscated_if_else.rs b/clippy_lints/src/methods/obfuscated_if_else.rs index 3e2cf113cb26..b71f79f84824 100644 --- a/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,5 +1,6 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; @@ -18,7 +19,12 @@ pub(super) fn check<'tcx>( let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { - let mut applicability = Applicability::MachineApplicable; + let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + let if_then = match then_method_name { "then" if let ExprKind::Closure(closure) = then_arg.kind => { let body = cx.tcx.hir().body(closure.body); diff --git a/tests/ui/obfuscated_if_else.fixed b/tests/ui/obfuscated_if_else.fixed index 11883a8989f8..bfe1c5e10cf8 100644 --- a/tests/ui/obfuscated_if_else.fixed +++ b/tests/ui/obfuscated_if_else.fixed @@ -1,5 +1,5 @@ #![warn(clippy::obfuscated_if_else)] -#![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::unnecessary_lazy_evaluations, clippy::unit_arg, clippy::unused_unit)] fn main() { if true { "a" } else { "b" }; @@ -11,4 +11,8 @@ fn main() { let partial = (a == 1).then_some("a"); partial.unwrap_or("b"); // not lint + + let mut a = 0; + if true { a += 1 } else { () }; + if true { () } else { a += 2 }; } diff --git a/tests/ui/obfuscated_if_else.rs b/tests/ui/obfuscated_if_else.rs index 1f7896e0ffae..0ded2a2ceedf 100644 --- a/tests/ui/obfuscated_if_else.rs +++ b/tests/ui/obfuscated_if_else.rs @@ -1,5 +1,5 @@ #![warn(clippy::obfuscated_if_else)] -#![allow(clippy::unnecessary_lazy_evaluations)] +#![allow(clippy::unnecessary_lazy_evaluations, clippy::unit_arg, clippy::unused_unit)] fn main() { true.then_some("a").unwrap_or("b"); @@ -11,4 +11,8 @@ fn main() { let partial = (a == 1).then_some("a"); partial.unwrap_or("b"); // not lint + + let mut a = 0; + true.then_some(a += 1).unwrap_or(()); + true.then_some(()).unwrap_or(a += 2); } diff --git a/tests/ui/obfuscated_if_else.stderr b/tests/ui/obfuscated_if_else.stderr index 33985d1111bf..9ce1f475c480 100644 --- a/tests/ui/obfuscated_if_else.stderr +++ b/tests/ui/obfuscated_if_else.stderr @@ -25,5 +25,17 @@ error: this method chain can be written more clearly with `if .. else ..` LL | (a == 1).then(|| "a").unwrap_or("b"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` -error: aborting due to 4 previous errors +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:16:5 + | +LL | true.then_some(a += 1).unwrap_or(()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { a += 1 } else { () }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:17:5 + | +LL | true.then_some(()).unwrap_or(a += 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { a += 2 }` + +error: aborting due to 6 previous errors From 3149e0ff83c8a0bbd87d8f2319d8b49eeb2ea605 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sat, 25 Jan 2025 23:15:10 +0200 Subject: [PATCH 156/342] Make proc_macro span's line & column 1-indexed, as documented That is, make the fake number that we return 1 and not 0. --- .../proc-macro-srv/src/server_impl/rust_analyzer_span.rs | 4 ++-- .../crates/proc-macro-srv/src/server_impl/token_id.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index c7614849e015..59293ee3f965 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -392,12 +392,12 @@ impl server::Span for RaSpanServer { fn line(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 0 + 1 } fn column(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 0 + 1 } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 466eb14b55ea..409cf3cc7813 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -291,11 +291,11 @@ impl server::Span for TokenIdServer { } fn line(&mut self, _span: Self::Span) -> usize { - 0 + 1 } fn column(&mut self, _span: Self::Span) -> usize { - 0 + 1 } } From 9ad59150e6ef304fcdb7d2e99136935122b7df98 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 26 Jan 2025 07:57:08 +0100 Subject: [PATCH 157/342] Update outdated permissions section in the README.md --- src/tools/rust-analyzer/docs/dev/README.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md index 3ba492e09597..c990212d585d 100644 --- a/src/tools/rust-analyzer/docs/dev/README.md +++ b/src/tools/rust-analyzer/docs/dev/README.md @@ -269,19 +269,13 @@ Note: we tag releases by dates, releasing a patch release on the same day should ## Permissions -There are three sets of people with extra permissions: +There are two sets of people with extra permissions: -* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads). - Admins have full access to the org. -* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization. - Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io. - They also have direct commit access, but all changes should via bors queue. +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer.toml). + This team has write access to the repository and merge queue permissions (note the repo itself is managed by infra admins). It's ok to self-approve if you think you know what you are doing! - bors should automatically sync the permissions. Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention. Don't feel pressured to review assigned PRs though. - If you don't feel like reviewing for whatever reason, someone else will pick the review up! -* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization. - This team can label and close issues. - -Note that at the time being you need to be a member of the org yourself to view the links. + If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)! +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)). + This team has general triaging permissions allowing to label, close and re-open issues. From ba78c227dc949633ef8cb96566acf1232db25074 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Sun, 26 Jan 2025 13:52:52 +0500 Subject: [PATCH 158/342] Remove "Known problems" section for `borrow_interior_mutable_const` --- clippy_lints/src/non_copy_const.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 8409d179b0f5..147654675ec9 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -89,16 +89,6 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// ### Known problems - /// When an enum has variants with interior mutability, use of its non - /// interior mutable variants can generate false positives. See issue - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) - /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issues - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) - /// /// ### Example /// ```no_run /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; From 25a77cf4f420d2a6fb7863297fab0615f4a4d2e9 Mon Sep 17 00:00:00 2001 From: Kalle Wachsmuth Date: Tue, 29 Oct 2024 20:49:22 +0100 Subject: [PATCH 159/342] remove `clippy::double_neg` --- .github/driver.sh | 6 +- book/src/usage.md | 9 +- clippy_dev/src/new_lint.rs | 4 +- clippy_lints/src/declared_lints.rs | 1 - clippy_lints/src/deprecated_lints.rs | 2 + clippy_lints/src/misc_early/double_neg.rs | 18 ---- clippy_lints/src/misc_early/mod.rs | 22 ---- tests/ui/double_neg.rs | 10 -- tests/ui/double_neg.stderr | 11 -- tests/ui/rename.fixed | 15 +-- tests/ui/rename.rs | 15 +-- tests/ui/rename.stderr | 126 +++++++++++----------- 12 files changed, 93 insertions(+), 146 deletions(-) delete mode 100644 clippy_lints/src/misc_early/double_neg.rs delete mode 100644 tests/ui/double_neg.rs delete mode 100644 tests/ui/double_neg.stderr diff --git a/.github/driver.sh b/.github/driver.sh index 09202b1878b2..701be6bd76d3 100755 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1 -sed -e "/= help: for/d" double_neg.stderr > normalized.stderr -diff -u normalized.stderr tests/ui/double_neg.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/box_default.rs 2>box_default.stderr && exit 1 +sed -e "/= help: for/d" box_default.stderr > normalized.stderr +diff -u normalized.stderr tests/ui/box_default.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=$(rustc --print sysroot) diff --git a/book/src/usage.md b/book/src/usage.md index 7a0be6994fe1..23edf6c2abaa 100644 --- a/book/src/usage.md +++ b/book/src/usage.md @@ -33,7 +33,7 @@ You can configure lint levels on the command line by adding `-A/W/D clippy::lint_name` like this: ```bash -cargo clippy -- -Aclippy::style -Wclippy::double_neg -Dclippy::perf +cargo clippy -- -Aclippy::style -Wclippy::box_default -Dclippy::perf ``` For [CI] all warnings can be elevated to errors which will in turn fail @@ -101,11 +101,10 @@ You can configure lint levels in source code the same way you can configure ```rust,ignore #![allow(clippy::style)] -#[warn(clippy::double_neg)] +#[warn(clippy::box_default)] fn main() { - let x = 1; - let y = --x; - // ^^ warning: double negation + let _ = Box::::new(Default::default()); + // ^ warning: `Box::new(_)` of default value } ``` diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 24d1a53266a0..35dd986ff614 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -455,7 +455,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> }); // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl - while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) { + while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) { let mut iter = iter .by_ref() .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); @@ -465,7 +465,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> // matches `!{` match_tokens!(iter, Bang OpenBrace); if let Some(LintDeclSearchResult { range, .. }) = - iter.find(|result| result.token == TokenKind::CloseBrace) + iter.find(|result| result.token_kind == TokenKind::CloseBrace) { last_decl_curly_offset = Some(range.end); } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3ff10d850f82..87e546fbf014 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -507,7 +507,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::misc::USED_UNDERSCORE_BINDING_INFO, crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, - crate::misc_early::DOUBLE_NEG_INFO, crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO, crate::misc_early::REDUNDANT_AT_REST_PATTERN_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 3ea792d8b835..5604172d6f30 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -129,6 +129,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::clone_double_ref", "suspicious_double_ref_op"), #[clippy::version = ""] ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = "1.86.0"] + ("clippy::double_neg", "double_negations"), #[clippy::version = ""] ("clippy::drop_bounds", "drop_bounds"), #[clippy::version = ""] diff --git a/clippy_lints/src/misc_early/double_neg.rs b/clippy_lints/src/misc_early/double_neg.rs deleted file mode 100644 index 06ba968fa4ed..000000000000 --- a/clippy_lints/src/misc_early/double_neg.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::{Expr, ExprKind, UnOp}; -use rustc_lint::EarlyContext; - -use super::DOUBLE_NEG; - -pub(super) fn check(cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind { - if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { - span_lint( - cx, - DOUBLE_NEG, - expr.span, - "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op", - ); - } - } -} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 37d7427f9a5d..637d6ed3ad2c 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,5 +1,4 @@ mod builtin_type_shadow; -mod double_neg; mod literal_suffix; mod mixed_case_hex_literals; mod redundant_at_rest_pattern; @@ -85,25 +84,6 @@ declare_clippy_lint! { "function arguments having names which only differ by an underscore" } -declare_clippy_lint! { - /// ### What it does - /// Detects expressions of the form `--x`. - /// - /// ### Why is this bad? - /// It can mislead C/C++ programmers to think `x` was - /// decremented. - /// - /// ### Example - /// ```no_run - /// let mut x = 3; - /// --x; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DOUBLE_NEG, - style, - "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++" -} - declare_clippy_lint! { /// ### What it does /// Warns on hexadecimal literals with mixed-case letter @@ -352,7 +332,6 @@ declare_clippy_lint! { declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, - DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX, SEPARATED_LITERAL_SUFFIX, @@ -415,7 +394,6 @@ impl EarlyLintPass for MiscEarlyLints { if let ExprKind::Lit(lit) = expr.kind { MiscEarlyLints::check_lit(cx, lit, expr.span); } - double_neg::check(cx, expr); } } diff --git a/tests/ui/double_neg.rs b/tests/ui/double_neg.rs deleted file mode 100644 index 3be8c6288738..000000000000 --- a/tests/ui/double_neg.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[warn(clippy::double_neg)] -#[allow(clippy::no_effect)] -fn main() { - let x = 1; - -x; - -(-x); - --x; - //~^ ERROR: `--x` could be misinterpreted as pre-decrement by C programmers, is usually - //~| NOTE: `-D clippy::double-neg` implied by `-D warnings` -} diff --git a/tests/ui/double_neg.stderr b/tests/ui/double_neg.stderr deleted file mode 100644 index 9a902d1323cc..000000000000 --- a/tests/ui/double_neg.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: `--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op - --> tests/ui/double_neg.rs:7:5 - | -LL | --x; - | ^^^ - | - = note: `-D clippy::double-neg` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::double_neg)]` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 47d6e119543d..501811fa491b 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -13,9 +13,8 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] -#![allow(unpredictable_function_pointer_comparisons)] +#![allow(clippy::manual_find_map)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] @@ -30,6 +29,7 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] #![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::missing_const_for_thread_local)] @@ -39,9 +39,11 @@ #![allow(invalid_reference_casting)] #![allow(suspicious_double_ref_op)] #![allow(invalid_nan_comparisons)] +#![allow(double_negations)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(unpredictable_function_pointer_comparisons)] #![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] @@ -60,8 +62,6 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(unpredictable_function_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -74,9 +74,8 @@ #![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type` #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map` -#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result` @@ -95,6 +94,7 @@ #![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter` #![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` @@ -104,9 +104,11 @@ #![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut` #![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref` #![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(double_negations)] //~ ERROR: lint `clippy::double_neg` #![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy` #![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref` +#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` #![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check` #![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option` #![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result` @@ -128,6 +130,5 @@ #![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label` #![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 12c7db69be2e..7f4b8062e1b4 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -13,9 +13,8 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::manual_find_map)] #![allow(clippy::manual_filter_map)] -#![allow(unpredictable_function_pointer_comparisons)] +#![allow(clippy::manual_find_map)] #![allow(clippy::useless_conversion)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::match_result_ok)] @@ -30,6 +29,7 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::panicking_overflow_checks)] #![allow(clippy::needless_borrow)] +#![allow(clippy::reversed_empty_ranges)] #![allow(clippy::single_char_add_str)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::missing_const_for_thread_local)] @@ -39,9 +39,11 @@ #![allow(invalid_reference_casting)] #![allow(suspicious_double_ref_op)] #![allow(invalid_nan_comparisons)] +#![allow(double_negations)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(unpredictable_function_pointer_comparisons)] #![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] @@ -60,8 +62,6 @@ #![allow(unknown_lints)] #![allow(unused_labels)] #![allow(ambiguous_wide_pointer_comparisons)] -#![allow(unpredictable_function_pointer_comparisons)] -#![allow(clippy::reversed_empty_ranges)] #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range` #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name` #![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr` @@ -74,9 +74,8 @@ #![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method` #![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type` #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence` -#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map` -#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` +#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map` #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion` #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching` #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result` @@ -95,6 +94,7 @@ #![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used` #![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else` #![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used` +#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` #![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str` #![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter` #![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const` @@ -104,9 +104,11 @@ #![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut` #![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref` #![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan` +#![warn(clippy::double_neg)] //~ ERROR: lint `clippy::double_neg` #![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds` #![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy` #![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref` +#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons` #![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check` #![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option` #![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result` @@ -128,6 +130,5 @@ #![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints` #![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label` #![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons` -#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop` fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 1ec45c4f1f7b..f24eaec3917a 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -73,132 +73,132 @@ error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_r LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` - --> tests/ui/rename.rs:77:9 - | -LL | #![warn(clippy::find_map)] - | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` - error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map` -error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` - --> tests/ui/rename.rs:79:9 +error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map` + --> tests/ui/rename.rs:78:9 | -LL | #![warn(clippy::fn_address_comparisons)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` +LL | #![warn(clippy::find_map)] + | ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::overflow_check_conditional)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` + --> tests/ui/rename.rs:97:9 + | +LL | #![warn(clippy::reverse_range_loop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` + error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` --> tests/ui/rename.rs:98:9 | @@ -253,155 +253,161 @@ error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` -error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` +error: lint `clippy::double_neg` has been renamed to `double_negations` --> tests/ui/rename.rs:107:9 | +LL | #![warn(clippy::double_neg)] + | ^^^^^^^^^^^^^^^^^^ help: use the new name: `double_negations` + +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` + --> tests/ui/rename.rs:108:9 + | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons` + --> tests/ui/rename.rs:111:9 + | +LL | #![warn(clippy::fn_address_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons` + error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:114:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:113:9 + --> tests/ui/rename.rs:115:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:114:9 + --> tests/ui/rename.rs:116:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:115:9 + --> tests/ui/rename.rs:117:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:116:9 + --> tests/ui/rename.rs:118:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:117:9 + --> tests/ui/rename.rs:119:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:118:9 + --> tests/ui/rename.rs:120:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:119:9 + --> tests/ui/rename.rs:121:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:120:9 + --> tests/ui/rename.rs:122:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:121:9 + --> tests/ui/rename.rs:123:9 | LL | #![warn(clippy::maybe_misused_cfg)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:122:9 + --> tests/ui/rename.rs:124:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs` - --> tests/ui/rename.rs:123:9 + --> tests/ui/rename.rs:125:9 | LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:124:9 + --> tests/ui/rename.rs:126:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:125:9 + --> tests/ui/rename.rs:127:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` - --> tests/ui/rename.rs:126:9 + --> tests/ui/rename.rs:128:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:127:9 + --> tests/ui/rename.rs:129:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:128:9 + --> tests/ui/rename.rs:130:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:129:9 + --> tests/ui/rename.rs:131:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:130:9 + --> tests/ui/rename.rs:132:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` -error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges` - --> tests/ui/rename.rs:131:9 - | -LL | #![warn(clippy::reverse_range_loop)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges` - -error: aborting due to 67 previous errors +error: aborting due to 68 previous errors From 93d347d7d25f227c56b4d829ae1bf3392b7c3db3 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Sun, 26 Jan 2025 12:48:33 +0100 Subject: [PATCH 160/342] Test pipes also when not running on Windows and Linux simultaneously Fixes https://github.com/rust-lang/rust/pull/135635#pullrequestreview-2574184488. --- library/std/src/io/pipe/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/pipe/tests.rs b/library/std/src/io/pipe/tests.rs index c1f3f192ca2d..f113b157459d 100644 --- a/library/std/src/io/pipe/tests.rs +++ b/library/std/src/io/pipe/tests.rs @@ -1,7 +1,7 @@ use crate::io::{Read, Write, pipe}; #[test] -#[cfg(all(windows, unix, not(miri)))] +#[cfg(all(any(unix, windows), not(miri)))] fn pipe_creation_clone_and_rw() { let (rx, tx) = pipe().unwrap(); From d3cd832ddadd4146f49c1e3e919d0eb4c6a3c732 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 22 Jan 2025 17:24:34 +0100 Subject: [PATCH 161/342] Document purpose of closure in from_fn.rs more clearly --- library/core/src/iter/sources/from_fn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 5f3d404d7dca..75cc0ffe3c77 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,7 @@ use crate::fmt; -/// Creates a new iterator where each iteration calls the provided closure -/// `F: FnMut() -> Option`. +/// Creates an iterator with the provided closure +/// `F: FnMut() -> Option` as its `[next](Iterator::next)` method. /// /// The iterator will yield the `T`s returned from the closure. /// From 0b818aa6731abff6878582130b2733617b2587d5 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Mon, 27 Jan 2025 04:54:40 +0900 Subject: [PATCH 162/342] fixed a missing description of the MSRV option for `manual_repeat_n` --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 999df0efaac1..b8f9fff9c613 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -750,6 +750,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison) * [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) * [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 4b0fcbfc3bc6..552141476f3a 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -619,6 +619,7 @@ define_Conf! { manual_pattern_char_comparison, manual_range_contains, manual_rem_euclid, + manual_repeat_n, manual_retain, manual_split_once, manual_str_repeat, From 57b5d3af620345c16d5450c9532ee99873995323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 22 Jan 2025 05:14:07 +0100 Subject: [PATCH 163/342] Compiler: Finalize dyn compatibility renaming --- .../rustc_hir_analysis/src/coherence/mod.rs | 3 +- .../src/multiple_supertrait_upcastable.rs | 2 +- .../ty/return_position_impl_trait_in_trait.rs | 2 +- .../src/error_reporting/traits/mod.rs | 3 +- .../src/traits/dyn_compatibility.rs | 6 +- .../rustc_trait_selection/src/traits/util.rs | 2 +- .../invalid_const_in_lifetime_position.stderr | 2 +- .../ice-generic-type-alias-105742.stderr | 2 +- .../associated-const-in-trait.stderr | 4 +- tests/ui/associated-item/issue-48027.stderr | 2 +- tests/ui/async-await/async-fn/dyn-pos.stderr | 2 +- .../in-trait/dyn-compatibility.stderr | 2 +- .../inference_var_self_argument.stderr | 2 +- ...impl-trait-for-trait-dyn-compatible.stderr | 2 +- .../const_param_ty_dyn_compatibility.stderr | 4 +- .../dyn-compatibility-err-ret.stderr | 4 +- .../dyn-compatibility-err-where-bounds.stderr | 4 +- .../generic_const_exprs/issue-102768.stderr | 2 +- ...reference-without-parens-suggestion.stderr | 2 +- .../almost-supertrait-associated-type.stderr | 6 +- .../associated-consts.curr.stderr | 4 +- ...-consts.dyn_compatible_for_dispatch.stderr | 2 +- .../avoid-ice-on-warning-2.old.stderr | 2 +- .../avoid-ice-on-warning-3.old.stderr | 4 +- .../bare-trait-dont-suggest-dyn.old.stderr | 2 +- tests/ui/dyn-compatibility/bounds.stderr | 2 +- .../gat-incompatible-supertrait.stderr | 2 +- .../ui/dyn-compatibility/generics.curr.stderr | 10 ++-- ...enerics.dyn_compatible_for_dispatch.stderr | 4 +- ...tion-correct-dyn-incompatible-trait.stderr | 4 +- .../mentions-Self-in-super-predicates.stderr | 6 +- .../mentions-Self.curr.stderr | 8 +-- ...ns-Self.dyn_compatible_for_dispatch.stderr | 4 +- .../missing-assoc-type.stderr | 2 +- .../dyn-compatibility/no-static.curr.stderr | 6 +- ...-static.dyn_compatible_for_dispatch.stderr | 2 +- .../ui/dyn-compatibility/sized-2.curr.stderr | 4 +- ...sized-2.dyn_compatible_for_dispatch.stderr | 2 +- tests/ui/dyn-compatibility/sized.curr.stderr | 4 +- .../sized.dyn_compatible_for_dispatch.stderr | 2 +- .../supertrait-mentions-GAT.stderr | 2 +- .../supertrait-mentions-Self.stderr | 2 +- .../taint-const-eval.curr.stderr | 6 +- ...st-eval.dyn_compatible_for_dispatch.stderr | 2 +- ...ble-receiver-and-wc-references-Self.stderr | 6 +- tests/ui/error-codes/E0038.stderr | 4 +- .../feature-gate-async-fn-in-dyn-trait.stderr | 6 +- ...gate-dispatch-from-dyn-missing-impl.stderr | 4 +- ...re-gate-dyn_compatible_for_dispatch.stderr | 10 ++-- ...t-in-trait-path-undeclared-lifetime.stderr | 2 +- .../gat-in-trait-path.base.stderr | 52 ----------------- .../gat-in-trait-path.stderr | 6 +- .../gat-trait-path-parenthesised-args.stderr | 4 +- .../issue-67510-pass.base.stderr | 19 ------- .../issue-67510-pass.stderr | 2 +- .../issue-67510.stderr | 2 +- .../issue-71176.stderr | 6 +- .../issue-76535.base.stderr | 57 ------------------- .../issue-76535.stderr | 4 +- .../issue-78671.base.stderr | 36 ------------ .../issue-78671.stderr | 2 +- .../issue-79422.base.stderr | 53 ----------------- .../issue-79422.stderr | 4 +- .../missing_lifetime_args.stderr | 2 +- ...it-path-type-error-once-implemented.stderr | 2 +- .../trait-objects.base.stderr | 51 ----------------- .../trait-objects.stderr | 6 +- .../trait-bounds/span-bug-issue-121597.stderr | 4 +- ...-trait-in-return-position-dyn-trait.stderr | 8 +-- ...ties-during-dyn-compatibility-check.stderr | 6 +- .../in-trait/dyn-compatibility.stderr | 8 +-- .../in-trait/foreign-dyn-error.stderr | 2 +- tests/ui/issues/issue-18959.stderr | 10 ++-- tests/ui/issues/issue-19380.stderr | 6 +- tests/ui/issues/issue-26056.stderr | 2 +- tests/ui/issues/issue-50781.stderr | 6 +- .../kindck-inherited-copy-bound.curr.stderr | 4 +- ...y-bound.dyn_compatible_for_dispatch.stderr | 2 +- tests/ui/resolve/issue-3907-2.stderr | 2 +- ...ry-self-types-dyn-incompatible.curr.stderr | 4 +- ...patible.dyn_compatible_for_dispatch.stderr | 2 +- .../unsizing-wfcheck-issue-127299.stderr | 6 +- ...-incompatible-trait-references-self.stderr | 4 +- ...mpatible-trait-should-use-self-2021.stderr | 4 +- ...-incompatible-trait-should-use-self.stderr | 4 +- ...atible-trait-should-use-where-sized.stderr | 2 +- tests/ui/suggestions/issue-116434-2015.stderr | 4 +- tests/ui/suggestions/issue-98500.stderr | 2 +- .../alias/generic-default-in-dyn.stderr | 4 +- tests/ui/traits/alias/object-fail.stderr | 2 +- .../alias/self-in-const-generics.stderr | 2 +- tests/ui/traits/alias/self-in-generics.stderr | 2 +- tests/ui/traits/issue-20692.stderr | 4 +- tests/ui/traits/issue-28576.stderr | 2 +- tests/ui/traits/issue-38404.stderr | 6 +- tests/ui/traits/issue-38604.stderr | 4 +- tests/ui/traits/issue-72410.stderr | 2 +- tests/ui/traits/item-privacy.stderr | 2 +- .../missing-for-type-in-impl.e2015.stderr | 2 +- .../supertrait-dyn-compatibility.stderr | 6 +- ...alize-fresh-infer-vars-issue-103626.stderr | 4 +- tests/ui/traits/object/macro-matcher.stderr | 2 +- tests/ui/traits/object/safety.stderr | 4 +- tests/ui/traits/test-2.stderr | 6 +- ...ter-defaults-referencing-Self-ppaux.stderr | 2 +- tests/ui/wf/issue-87495.stderr | 2 +- ...-convert-dyn-incompat-trait-obj-box.stderr | 6 +- .../wf-convert-dyn-incompat-trait-obj.stderr | 6 +- .../wf/wf-dyn-incompat-trait-obj-match.stderr | 4 +- tests/ui/wf/wf-dyn-incompatible.stderr | 2 +- tests/ui/wf/wf-fn-where-clause.stderr | 2 +- 111 files changed, 193 insertions(+), 463 deletions(-) delete mode 100644 tests/ui/generic-associated-types/gat-in-trait-path.base.stderr delete mode 100644 tests/ui/generic-associated-types/issue-67510-pass.base.stderr delete mode 100644 tests/ui/generic-associated-types/issue-76535.base.stderr delete mode 100644 tests/ui/generic-associated-types/issue-78671.base.stderr delete mode 100644 tests/ui/generic-associated-types/issue-79422.base.stderr delete mode 100644 tests/ui/generic-associated-types/trait-objects.base.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 4e5f0a3186a9..07ba6033a9f5 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -199,10 +199,9 @@ fn check_object_overlap<'tcx>( for component_def_id in component_def_ids { if !tcx.is_dyn_compatible(component_def_id) { - // FIXME(dyn_compat_renaming): Rename test and update comment. // Without the 'dyn_compatible_for_dispatch' feature this is an error // which will be reported by wfcheck. Ignore it here. - // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`. + // This is tested by `coherence-impl-trait-for-trait-dyn-compatible.rs`. // With the feature enabled, the trait is not implemented automatically, // so this is valid. } else { diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 9fde35f82d82..35db8632625e 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -37,7 +37,7 @@ declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTAB impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let def_id = item.owner_id.to_def_id(); - // NOTE(nbdd0121): use `object_safety_violations` instead of `is_dyn_compatible` because + // NOTE(nbdd0121): use `dyn_compatibility_violations` instead of `is_dyn_compatible` because // the latter will report `where_clause_object_safety` lint. if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind && cx.tcx.is_dyn_compatible(def_id) diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs index 21c605f8296d..fc9ae0ab1b38 100644 --- a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -4,7 +4,7 @@ use crate::ty::{self, ExistentialPredicateStableCmpExt, TyCtxt}; impl<'tcx> TyCtxt<'tcx> { /// Given a `def_id` of a trait or impl method, compute whether that method needs to - /// have an RPITIT shim applied to it for it to be object safe. If so, return the + /// have an RPITIT shim applied to it for it to be dyn compatible. If so, return the /// `def_id` of the RPITIT, and also the args of trait method that returns the RPITIT. /// /// NOTE that these args are not, in general, the same as than the RPITIT's args. They diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 8111983c5398..b4e5cc6185cf 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -482,11 +482,10 @@ pub fn report_dyn_incompatibility<'tcx>( for (span, msg) in iter::zip(multi_span, messages) { note_span.push_span_label(span, msg); } - // FIXME(dyn_compat_renaming): Update the URL. err.span_note( note_span, "for a trait to be dyn compatible it needs to allow building a vtable\n\ - for more information, visit ", + for more information, visit ", ); // Only provide the help if its a local trait, otherwise it's not actionable. diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 66491d9abe1a..baa94ead9d48 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -64,7 +64,7 @@ fn is_dyn_compatible(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { } /// We say a method is *vtable safe* if it can be invoked on a trait -/// object. Note that object-safe traits can have some +/// object. Note that dyn-compatible traits can have some /// non-vtable-safe methods, so long as they require `Self: Sized` or /// otherwise ensure that they cannot be used when `Self = Trait`. pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool { @@ -421,7 +421,7 @@ fn virtual_call_violations_for_method<'tcx>( let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. - // However, this is already considered object-safe. We allow it as a special case here. + // However, this is already considered dyn compatible. We allow it as a special case here. // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows // `Receiver: Unsize dyn Trait]>`. if receiver_ty != tcx.types.self_param { @@ -631,7 +631,7 @@ fn object_ty_for_trait<'tcx>( /// - `self: Pin>` requires `Pin>: DispatchFromDyn>>`. /// /// The only case where the receiver is not dispatchable, but is still a valid receiver -/// type (just not object-safe), is when there is more than one level of pointer indirection. +/// type (just not dyn compatible), is when there is more than one level of pointer indirection. /// E.g., `self: &&Self`, `self: &Rc`, `self: Box>`. In these cases, there /// is no way, or at least no inexpensive way, to coerce the receiver from the version where /// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index c9fb2a757e17..d30363ec1589 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -25,7 +25,7 @@ use tracing::debug; /// trait Bar {} /// trait Foo = Bar + Bar; /// -/// let not_object_safe: dyn Foo; // bad, two `Bar` principals. +/// let dyn_incompatible: dyn Foo; // bad, two `Bar` principals. /// ``` pub fn expand_trait_aliases<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr b/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr index 180ba63927b2..2c25589a5531 100644 --- a/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr +++ b/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr @@ -99,7 +99,7 @@ LL | fn f<'a>(arg : Box = &'a ()>>) {} | ^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/invalid_const_in_lifetime_position.rs:2:10 | LL | trait X { diff --git a/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr b/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr index 72d1a52f710b..1e52b699820b 100644 --- a/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr +++ b/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr @@ -301,7 +301,7 @@ LL | pub fn next<'a, T>(s: &'a mut dyn SVec) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `SVec` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/ice-generic-type-alias-105742.rs:15:17 | LL | pub trait SVec: Index< diff --git a/tests/ui/associated-consts/associated-const-in-trait.stderr b/tests/ui/associated-consts/associated-const-in-trait.stderr index 107ceeaf1134..5aaa6f6be057 100644 --- a/tests/ui/associated-consts/associated-const-in-trait.stderr +++ b/tests/ui/associated-consts/associated-const-in-trait.stderr @@ -5,7 +5,7 @@ LL | impl dyn Trait { | ^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/associated-const-in-trait.rs:4:11 | LL | trait Trait { @@ -21,7 +21,7 @@ LL | const fn n() -> usize { Self::N } | ^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/associated-const-in-trait.rs:4:11 | LL | trait Trait { diff --git a/tests/ui/associated-item/issue-48027.stderr b/tests/ui/associated-item/issue-48027.stderr index 1baaefd77204..513961e2bd0a 100644 --- a/tests/ui/associated-item/issue-48027.stderr +++ b/tests/ui/associated-item/issue-48027.stderr @@ -5,7 +5,7 @@ LL | impl dyn Bar {} | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-48027.rs:2:11 | LL | trait Bar { diff --git a/tests/ui/async-await/async-fn/dyn-pos.stderr b/tests/ui/async-await/async-fn/dyn-pos.stderr index f9d2a6694775..7d5b37bdbe73 100644 --- a/tests/ui/async-await/async-fn/dyn-pos.stderr +++ b/tests/ui/async-await/async-fn/dyn-pos.stderr @@ -5,7 +5,7 @@ LL | fn foo(x: &dyn AsyncFn()) {} | ^^^^^^^^^ `AsyncFnMut` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait is not dyn compatible because it contains the generic associated type `CallRefFuture` diff --git a/tests/ui/async-await/in-trait/dyn-compatibility.stderr b/tests/ui/async-await/in-trait/dyn-compatibility.stderr index c6c406902f69..553bcbf89d59 100644 --- a/tests/ui/async-await/in-trait/dyn-compatibility.stderr +++ b/tests/ui/async-await/in-trait/dyn-compatibility.stderr @@ -5,7 +5,7 @@ LL | let x: &dyn Foo = todo!(); | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility.rs:5:14 | LL | trait Foo { diff --git a/tests/ui/async-await/inference_var_self_argument.stderr b/tests/ui/async-await/inference_var_self_argument.stderr index a674fc0f3a59..1fccc32470ff 100644 --- a/tests/ui/async-await/inference_var_self_argument.stderr +++ b/tests/ui/async-await/inference_var_self_argument.stderr @@ -14,7 +14,7 @@ LL | async fn foo(self: &dyn Foo) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/inference_var_self_argument.rs:5:14 | LL | trait Foo { diff --git a/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr b/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr index 20257bbaf285..033bfee226fd 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr @@ -5,7 +5,7 @@ LL | impl DynIncompatible for dyn DynIncompatible { } | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/coherence-impl-trait-for-trait-dyn-compatible.rs:6:45 | LL | trait DynIncompatible { fn eq(&self, other: Self); } diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr index cd7f3a3c21d0..6fa9b591ad26 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr @@ -5,7 +5,7 @@ LL | fn foo(a: &dyn ConstParamTy_) {} | ^^^^^^^^^^^^^ `ConstParamTy_` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | = note: the trait is not dyn compatible because it uses `Self` as a type parameter @@ -21,7 +21,7 @@ LL | fn bar(a: &dyn UnsizedConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | = note: the trait is not dyn compatible because it uses `Self` as a type parameter diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr index 763bc626c9d3..8bc6ef093d04 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr @@ -5,7 +5,7 @@ LL | fn use_dyn(v: &dyn Foo) { | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility-err-ret.rs:8:8 | LL | trait Foo { @@ -24,7 +24,7 @@ LL | v.test(); | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility-err-ret.rs:8:8 | LL | trait Foo { diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr index 56678e4e9af4..f5eaaa37916d 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr @@ -5,7 +5,7 @@ LL | fn use_dyn(v: &dyn Foo) { | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility-err-where-bounds.rs:8:8 | LL | trait Foo { @@ -22,7 +22,7 @@ LL | v.test(); | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility-err-where-bounds.rs:8:8 | LL | trait Foo { diff --git a/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr b/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr index bd1811bd2cc6..57b61006631f 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr @@ -99,7 +99,7 @@ LL | fn f2<'a>(arg: Box = &'a ()>>) {} | ^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-102768.rs:5:10 | LL | trait X { diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 7994ddf11c3e..4fee6cc9a222 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -28,7 +28,7 @@ LL | let _: &Copy + 'static; | = note: the trait is not dyn compatible because it requires `Self: Sized` = note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit error: aborting due to 3 previous errors diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr index f241333f2a76..a384697ee083 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr @@ -5,7 +5,7 @@ LL | impl Dyn for dyn Foo + '_ { | ^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/almost-supertrait-associated-type.rs:33:34 | LL | trait Foo: Super @@ -22,7 +22,7 @@ LL | (&PhantomData:: as &dyn Foo).transmute(t) | ^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/almost-supertrait-associated-type.rs:33:34 | LL | trait Foo: Super @@ -39,7 +39,7 @@ LL | (&PhantomData:: as &dyn Foo).transmute(t) | ^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/almost-supertrait-associated-type.rs:33:34 | LL | trait Foo: Super diff --git a/tests/ui/dyn-compatibility/associated-consts.curr.stderr b/tests/ui/dyn-compatibility/associated-consts.curr.stderr index 45d4f7955424..de2439381239 100644 --- a/tests/ui/dyn-compatibility/associated-consts.curr.stderr +++ b/tests/ui/dyn-compatibility/associated-consts.curr.stderr @@ -5,7 +5,7 @@ LL | fn make_bar(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/associated-consts.rs:9:11 | LL | trait Bar { @@ -21,7 +21,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/associated-consts.rs:9:11 | LL | trait Bar { diff --git a/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr index 4c8c82196ed2..704d833f00ba 100644 --- a/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/associated-consts.rs:9:11 | LL | trait Bar { diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr index ff5e9fdb6b36..b811ef40c26b 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr @@ -34,7 +34,7 @@ LL | fn id(f: Copy) -> usize { | = note: the trait is not dyn compatible because it requires `Self: Sized` = note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit error[E0618]: expected function, found `(dyn Copy + 'static)` --> $DIR/avoid-ice-on-warning-2.rs:12:5 diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr index 92a2d3401152..d8935be56094 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr @@ -72,7 +72,7 @@ LL | trait B { fn f(a: A) -> A; } | ^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/avoid-ice-on-warning-3.rs:14:14 | LL | trait A { fn g(b: B) -> B; } @@ -109,7 +109,7 @@ LL | trait A { fn g(b: B) -> B; } | ^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/avoid-ice-on-warning-3.rs:4:14 | LL | trait B { fn f(a: A) -> A; } diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr index e3ec5b9c3c86..7be6cb0d03bb 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr @@ -23,7 +23,7 @@ LL | fn ord_prefer_dot(s: String) -> Ord { | ^^^ `Ord` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | = note: the trait is not dyn compatible because it uses `Self` as a type parameter diff --git a/tests/ui/dyn-compatibility/bounds.stderr b/tests/ui/dyn-compatibility/bounds.stderr index d45e66b1d5ef..5473af388c0c 100644 --- a/tests/ui/dyn-compatibility/bounds.stderr +++ b/tests/ui/dyn-compatibility/bounds.stderr @@ -5,7 +5,7 @@ LL | fn f() -> Box> { | ^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/bounds.rs:4:13 | LL | trait X { diff --git a/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr index 04dc0b1d6f41..cfebd5d69470 100644 --- a/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr +++ b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr @@ -5,7 +5,7 @@ LL | fn take_dyn(_: &dyn Child) {} | ^^^^^ `Super` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-incompatible-supertrait.rs:10:10 | LL | trait Super { diff --git a/tests/ui/dyn-compatibility/generics.curr.stderr b/tests/ui/dyn-compatibility/generics.curr.stderr index 1607954ab704..d29d02b2ff3c 100644 --- a/tests/ui/dyn-compatibility/generics.curr.stderr +++ b/tests/ui/dyn-compatibility/generics.curr.stderr @@ -5,7 +5,7 @@ LL | fn make_bar(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { @@ -21,7 +21,7 @@ LL | fn make_bar_explicit(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { @@ -37,7 +37,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { @@ -54,7 +54,7 @@ LL | t as &dyn Bar | ^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { @@ -70,7 +70,7 @@ LL | t as &dyn Bar | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { diff --git a/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr index 7f31b29b39c2..b3565a766fe1 100644 --- a/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { @@ -22,7 +22,7 @@ LL | t as &dyn Bar | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { diff --git a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr index 1ed78e1e6594..bb3f7899bf1c 100644 --- a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr +++ b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr @@ -5,7 +5,7 @@ LL | let test: &mut dyn Bar = &mut thing; | ^^^^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mention-correct-dyn-incompatible-trait.rs:4:8 | LL | fn foo(&self, val: T); @@ -23,7 +23,7 @@ LL | let test: &mut dyn Bar = &mut thing; | ^^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mention-correct-dyn-incompatible-trait.rs:4:8 | LL | fn foo(&self, val: T); diff --git a/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr b/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr index eba2c15dd74f..cf3a7b230847 100644 --- a/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr @@ -5,7 +5,7 @@ LL | elements: Vec>, | ^^^^ `Expr` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self-in-super-predicates.rs:5:21 | LL | trait Expr: Debug + PartialEq { @@ -20,7 +20,7 @@ LL | let a: Box = Box::new(SExpr::new()); | ^^^^ `Expr` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self-in-super-predicates.rs:5:21 | LL | trait Expr: Debug + PartialEq { @@ -35,7 +35,7 @@ LL | let b: Box = Box::new(SExpr::new()); | ^^^^ `Expr` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self-in-super-predicates.rs:5:21 | LL | trait Expr: Debug + PartialEq { diff --git a/tests/ui/dyn-compatibility/mentions-Self.curr.stderr b/tests/ui/dyn-compatibility/mentions-Self.curr.stderr index 90db86ffef9b..2d3fe5ce636c 100644 --- a/tests/ui/dyn-compatibility/mentions-Self.curr.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self.curr.stderr @@ -5,7 +5,7 @@ LL | fn make_bar(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self.rs:11:22 | LL | trait Bar { @@ -21,7 +21,7 @@ LL | fn make_baz(t: &T) -> &dyn Baz { | ^^^^^^^ `Baz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self.rs:15:22 | LL | trait Baz { @@ -37,7 +37,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self.rs:11:22 | LL | trait Bar { @@ -54,7 +54,7 @@ LL | t | ^ `Baz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self.rs:15:22 | LL | trait Baz { diff --git a/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr index 4a50d3f07e4c..91c26a860259 100644 --- a/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self.rs:11:22 | LL | trait Bar { @@ -22,7 +22,7 @@ LL | t | ^ `Baz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/mentions-Self.rs:15:22 | LL | trait Baz { diff --git a/tests/ui/dyn-compatibility/missing-assoc-type.stderr b/tests/ui/dyn-compatibility/missing-assoc-type.stderr index 3f550494b338..5a7560682f2e 100644 --- a/tests/ui/dyn-compatibility/missing-assoc-type.stderr +++ b/tests/ui/dyn-compatibility/missing-assoc-type.stderr @@ -5,7 +5,7 @@ LL | fn bar(x: &dyn Foo) {} | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/missing-assoc-type.rs:2:10 | LL | trait Foo { diff --git a/tests/ui/dyn-compatibility/no-static.curr.stderr b/tests/ui/dyn-compatibility/no-static.curr.stderr index 867c485053d5..bf9b48e7a43d 100644 --- a/tests/ui/dyn-compatibility/no-static.curr.stderr +++ b/tests/ui/dyn-compatibility/no-static.curr.stderr @@ -5,7 +5,7 @@ LL | fn diverges() -> Box { | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { @@ -29,7 +29,7 @@ LL | let b: Box = Box::new(Bar); | ^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { @@ -53,7 +53,7 @@ LL | let b: Box = Box::new(Bar); | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { diff --git a/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr index 65608a9cca71..d5ad45103346 100644 --- a/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | let b: Box = Box::new(Bar); | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { diff --git a/tests/ui/dyn-compatibility/sized-2.curr.stderr b/tests/ui/dyn-compatibility/sized-2.curr.stderr index c8fd10562373..2d262072d5df 100644 --- a/tests/ui/dyn-compatibility/sized-2.curr.stderr +++ b/tests/ui/dyn-compatibility/sized-2.curr.stderr @@ -5,7 +5,7 @@ LL | fn make_bar(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/sized-2.rs:9:18 | LL | trait Bar @@ -20,7 +20,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/sized-2.rs:9:18 | LL | trait Bar diff --git a/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr index 477dacdf5a1b..1fbc10c0c3f8 100644 --- a/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/sized-2.rs:9:18 | LL | trait Bar diff --git a/tests/ui/dyn-compatibility/sized.curr.stderr b/tests/ui/dyn-compatibility/sized.curr.stderr index d86ea9197b9a..a197d9670051 100644 --- a/tests/ui/dyn-compatibility/sized.curr.stderr +++ b/tests/ui/dyn-compatibility/sized.curr.stderr @@ -5,7 +5,7 @@ LL | fn make_bar(t: &T) -> &dyn Bar { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/sized.rs:8:12 | LL | trait Bar: Sized { @@ -20,7 +20,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/sized.rs:8:12 | LL | trait Bar: Sized { diff --git a/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr index b763173594b8..350c8992c6f9 100644 --- a/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | t | ^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/sized.rs:8:12 | LL | trait Bar: Sized { diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr index f5dea2564698..8e139ee6b48b 100644 --- a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr +++ b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr @@ -27,7 +27,7 @@ LL | fn c(&self) -> dyn SuperTrait; | ^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/supertrait-mentions-GAT.rs:4:10 | LL | type Gat<'a> diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr b/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr index f9ef0c9b2e04..a763649e9c64 100644 --- a/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr +++ b/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr @@ -25,7 +25,7 @@ LL | fn make_baz(t: &T) -> &dyn Baz { | ^^^ `Baz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/supertrait-mentions-Self.rs:8:13 | LL | trait Baz : Bar { diff --git a/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr b/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr index 8442314835ea..7e80a1d2e427 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr +++ b/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr @@ -5,7 +5,7 @@ LL | static FOO: &(dyn Qux + Sync) = "desc"; | ^^^^^^^^^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { @@ -28,7 +28,7 @@ LL | static FOO: &(dyn Qux + Sync) = "desc"; | ^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { @@ -52,7 +52,7 @@ LL | static FOO: &(dyn Qux + Sync) = "desc"; | ^^^^^^^^^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { diff --git a/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr index 1c51df8501f1..0bc7d0b14d3d 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | static FOO: &(dyn Qux + Sync) = "desc"; | ^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { diff --git a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr index 45a924008c7e..1299167159e2 100644 --- a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr +++ b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr @@ -8,7 +8,7 @@ LL | fn fetcher() -> Box { | ^^^^^^^^^^^ `Fetcher` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 | LL | pub trait Fetcher: Send + Sync { @@ -26,7 +26,7 @@ LL | let fetcher = fetcher(); | ^^^^^^^^^ `Fetcher` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 | LL | pub trait Fetcher: Send + Sync { @@ -44,7 +44,7 @@ LL | let _ = fetcher.get(); | ^^^^^^^^^^^^^ `Fetcher` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 | LL | pub trait Fetcher: Send + Sync { diff --git a/tests/ui/error-codes/E0038.stderr b/tests/ui/error-codes/E0038.stderr index 59e9f504d17b..63a5249a3864 100644 --- a/tests/ui/error-codes/E0038.stderr +++ b/tests/ui/error-codes/E0038.stderr @@ -5,7 +5,7 @@ LL | fn call_foo(x: Box) { | ^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/E0038.rs:2:22 | LL | trait Trait { @@ -21,7 +21,7 @@ LL | let y = x.foo(); | ^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/E0038.rs:2:22 | LL | trait Trait { diff --git a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr index b4de6b664693..ab8c092a8265 100644 --- a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr +++ b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr @@ -5,7 +5,7 @@ LL | async fn takes_dyn_trait(x: &dyn Foo) { | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 | LL | trait Foo { @@ -21,7 +21,7 @@ LL | x.bar().await; | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 | LL | trait Foo { @@ -37,7 +37,7 @@ LL | x.bar().await; | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 | LL | trait Foo { diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr index f8fc086c4414..6634ce121186 100644 --- a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr @@ -8,7 +8,7 @@ LL | Ptr(Box::new(4)) as Ptr; | ^^^^^^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 | LL | trait Trait { @@ -27,7 +27,7 @@ LL | Ptr(Box::new(4)) as Ptr; | ^^^^^^^^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 | LL | trait Trait { diff --git a/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr b/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr index 10540f0219d4..2c3edd6e6a5f 100644 --- a/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr +++ b/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr @@ -5,7 +5,7 @@ LL | fn takes_dyn_incompatible_ref(obj: &dyn DynIncompatible1) { | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible1` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:4:25 | LL | trait DynIncompatible1: Sized {} @@ -20,7 +20,7 @@ LL | fn return_dyn_incompatible_ref() -> &'static dyn DynIncompatible2 { | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible2` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:7:8 | LL | trait DynIncompatible2 { @@ -43,7 +43,7 @@ LL | fn takes_dyn_incompatible_box(obj: Box) { | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible3` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:11:8 | LL | trait DynIncompatible3 { @@ -59,7 +59,7 @@ LL | fn return_dyn_incompatible_rc() -> std::rc::Rc { | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible4` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:15:22 | LL | trait DynIncompatible4 { @@ -75,7 +75,7 @@ LL | impl Trait for dyn DynIncompatible1 {} | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible1` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:4:25 | LL | trait DynIncompatible1: Sized {} diff --git a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr index 4c5a47e73c6c..c9b46de5c33c 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr @@ -43,7 +43,7 @@ LL | fn _f(arg : Box X = &'a [u32]>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:2:8 | LL | trait X { diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr deleted file mode 100644 index b2b569e6261b..000000000000 --- a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:26:17 - | -LL | fn f(_arg : Box Foo = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:32:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:32:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = note: required for the cast from `Box>` to `Box<(dyn Foo = &'a ()> + 'static)>` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.stderr index df79556c825a..e57f6b48401f 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path.stderr @@ -5,7 +5,7 @@ LL | fn f(_arg : Box Foo = &'a ()>>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-in-trait-path.rs:6:10 | LL | trait Foo { @@ -21,7 +21,7 @@ LL | f(Box::new(foo)); | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-in-trait-path.rs:6:10 | LL | trait Foo { @@ -37,7 +37,7 @@ LL | f(Box::new(foo)); | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-in-trait-path.rs:6:10 | LL | trait Foo { diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr index 499ce8e4a321..52fed354edf8 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr @@ -130,7 +130,7 @@ LL | fn foo<'a>(arg: Box>) {} | ^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 | LL | trait X { @@ -196,7 +196,7 @@ LL | fn bar<'a>(arg: Box>) {} | ^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 | LL | trait X { diff --git a/tests/ui/generic-associated-types/issue-67510-pass.base.stderr b/tests/ui/generic-associated-types/issue-67510-pass.base.stderr deleted file mode 100644 index 563089489694..000000000000 --- a/tests/ui/generic-associated-types/issue-67510-pass.base.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-67510-pass.rs:12:23 - | -LL | fn _func1<'a>(_x: Box=&'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-67510-pass.rs:9:10 - | -LL | trait X { - | - this trait is not dyn compatible... -LL | type Y<'a>; - | ^ ...because it contains the generic associated type `Y` - = help: consider moving `Y` to another trait - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-67510-pass.stderr b/tests/ui/generic-associated-types/issue-67510-pass.stderr index f6846f833fea..4b56c4ef35f4 100644 --- a/tests/ui/generic-associated-types/issue-67510-pass.stderr +++ b/tests/ui/generic-associated-types/issue-67510-pass.stderr @@ -5,7 +5,7 @@ LL | fn _func1<'a>(_x: Box=&'a ()>>) {} | ^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-67510-pass.rs:4:10 | LL | trait X { diff --git a/tests/ui/generic-associated-types/issue-67510.stderr b/tests/ui/generic-associated-types/issue-67510.stderr index e8555a7aa1fd..f5c494788ea5 100644 --- a/tests/ui/generic-associated-types/issue-67510.stderr +++ b/tests/ui/generic-associated-types/issue-67510.stderr @@ -36,7 +36,7 @@ LL | fn f(x: Box = &'a ()>>) {} | ^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-67510.rs:2:10 | LL | trait X { diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index a78151384d4b..56439f6dfea8 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -55,7 +55,7 @@ LL | inner: Box>, | ^^^^^^^^^^^^^^^^^^^ `Provider` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-71176.rs:2:10 | LL | trait Provider { @@ -72,7 +72,7 @@ LL | inner: Box::new(()), | ^^^^^^^^^^^^ `Provider` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-71176.rs:2:10 | LL | trait Provider { @@ -89,7 +89,7 @@ LL | inner: Box::new(()), | ^^^^^^^^^^^^ `Provider` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-71176.rs:2:10 | LL | trait Provider { diff --git a/tests/ui/generic-associated-types/issue-76535.base.stderr b/tests/ui/generic-associated-types/issue-76535.base.stderr deleted file mode 100644 index b503fad2d84f..000000000000 --- a/tests/ui/generic-associated-types/issue-76535.base.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error[E0107]: missing generics for associated type `SuperTrait::SubType` - --> $DIR/issue-76535.rs:39:33 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-76535.rs:9:10 - | -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ -- -help: add missing lifetime argument - | -LL | let sub: Box = SubStruct>> = Box::new(SuperStruct::new(0)); - | ++++ - -error[E0038]: the trait `SuperTrait` is not dyn compatible - --> $DIR/issue-76535.rs:39:14 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-76535.rs:9:10 - | -LL | pub trait SuperTrait { - | ---------- this trait is not dyn compatible... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` implements `SuperTrait` within this crate. Consider using it directly instead. - = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - -error[E0038]: the trait `SuperTrait` is not dyn compatible - --> $DIR/issue-76535.rs:39:57 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-76535.rs:9:10 - | -LL | pub trait SuperTrait { - | ---------- this trait is not dyn compatible... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` implements `SuperTrait` within this crate. Consider using it directly instead. - = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - = note: required for the cast from `Box` to `Box = SubStruct<'_>>>` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-76535.stderr b/tests/ui/generic-associated-types/issue-76535.stderr index 6b7c3bfe7312..b828234afa12 100644 --- a/tests/ui/generic-associated-types/issue-76535.stderr +++ b/tests/ui/generic-associated-types/issue-76535.stderr @@ -21,7 +21,7 @@ LL | let sub: Box> = Box::new(SuperStruc | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-76535.rs:4:10 | LL | pub trait SuperTrait { @@ -39,7 +39,7 @@ LL | let sub: Box> = Box::new(SuperStruc | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-76535.rs:4:10 | LL | pub trait SuperTrait { diff --git a/tests/ui/generic-associated-types/issue-78671.base.stderr b/tests/ui/generic-associated-types/issue-78671.base.stderr deleted file mode 100644 index 9bfe8c0b9561..000000000000 --- a/tests/ui/generic-associated-types/issue-78671.base.stderr +++ /dev/null @@ -1,36 +0,0 @@ -error[E0107]: missing generics for associated type `CollectionFamily::Member` - --> $DIR/issue-78671.rs:10:47 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^ expected 1 generic argument - | -note: associated type defined here, with 1 generic parameter: `T` - --> $DIR/issue-78671.rs:7:10 - | -LL | type Member; - | ^^^^^^ - -help: add missing generic argument - | -LL | Box::new(Family) as &dyn CollectionFamily=usize> - | +++ - -error[E0038]: the trait `CollectionFamily` is not dyn compatible - --> $DIR/issue-78671.rs:10:25 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-78671.rs:7:10 - | -LL | trait CollectionFamily { - | ---------------- this trait is not dyn compatible... -LL | type Member; - | ^^^^^^ ...because it contains the generic associated type `Member` - = help: consider moving `Member` to another trait - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-78671.stderr b/tests/ui/generic-associated-types/issue-78671.stderr index c85e97067cbc..c6da137672de 100644 --- a/tests/ui/generic-associated-types/issue-78671.stderr +++ b/tests/ui/generic-associated-types/issue-78671.stderr @@ -21,7 +21,7 @@ LL | Box::new(Family) as &dyn CollectionFamily | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-78671.rs:2:10 | LL | trait CollectionFamily { diff --git a/tests/ui/generic-associated-types/issue-79422.base.stderr b/tests/ui/generic-associated-types/issue-79422.base.stderr deleted file mode 100644 index c3de2b71762e..000000000000 --- a/tests/ui/generic-associated-types/issue-79422.base.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error[E0107]: missing generics for associated type `MapLike::VRefCont` - --> $DIR/issue-79422.rs:47:36 - | -LL | as Box>>; - | ^^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-79422.rs:23:10 - | -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ -- -help: add missing lifetime argument - | -LL | as Box = dyn RefCont<'_, u8>>>; - | ++++ - -error[E0038]: the trait `MapLike` is not dyn compatible - --> $DIR/issue-79422.rs:47:12 - | -LL | as Box>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-79422.rs:23:10 - | -LL | trait MapLike { - | ------- this trait is not dyn compatible... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - -error[E0038]: the trait `MapLike` is not dyn compatible - --> $DIR/issue-79422.rs:44:13 - | -LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-79422.rs:23:10 - | -LL | trait MapLike { - | ------- this trait is not dyn compatible... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-79422.stderr b/tests/ui/generic-associated-types/issue-79422.stderr index a81217e96c38..6311e4de272d 100644 --- a/tests/ui/generic-associated-types/issue-79422.stderr +++ b/tests/ui/generic-associated-types/issue-79422.stderr @@ -21,7 +21,7 @@ LL | as Box>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-79422.rs:18:10 | LL | trait MapLike { @@ -37,7 +37,7 @@ LL | let m = Box::new(std::collections::BTreeMap::::new()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-79422.rs:18:10 | LL | trait MapLike { diff --git a/tests/ui/generic-associated-types/missing_lifetime_args.stderr b/tests/ui/generic-associated-types/missing_lifetime_args.stderr index 6b8df5cc12f9..7b6888817f4f 100644 --- a/tests/ui/generic-associated-types/missing_lifetime_args.stderr +++ b/tests/ui/generic-associated-types/missing_lifetime_args.stderr @@ -55,7 +55,7 @@ LL | fn foo<'c, 'd>(_arg: Box>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/missing_lifetime_args.rs:2:10 | LL | trait X { diff --git a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr index 5c9e9dbe3d7d..45bca1a76287 100644 --- a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr +++ b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr @@ -99,7 +99,7 @@ LL | fn f2<'a>(arg : Box = &'a ()>>) {} | ^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/trait-path-type-error-once-implemented.rs:2:10 | LL | trait X { diff --git a/tests/ui/generic-associated-types/trait-objects.base.stderr b/tests/ui/generic-associated-types/trait-objects.base.stderr deleted file mode 100644 index fe9ab165d4af..000000000000 --- a/tests/ui/generic-associated-types/trait-objects.base.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:13:21 - | -LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:15:7 - | -LL | x.size_hint().0 - | ^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:15:5 - | -LL | x.size_hint().0 - | ^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/trait-objects.stderr b/tests/ui/generic-associated-types/trait-objects.stderr index 56a1cb1906fb..7d95718ec874 100644 --- a/tests/ui/generic-associated-types/trait-objects.stderr +++ b/tests/ui/generic-associated-types/trait-objects.stderr @@ -5,7 +5,7 @@ LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> u | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/trait-objects.rs:2:10 | LL | trait StreamingIterator { @@ -21,7 +21,7 @@ LL | x.size_hint().0 | ^^^^^^^^^ `StreamingIterator` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/trait-objects.rs:2:10 | LL | trait StreamingIterator { @@ -37,7 +37,7 @@ LL | x.size_hint().0 | ^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/trait-objects.rs:2:10 | LL | trait StreamingIterator { diff --git a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr index fc3d9c2171d2..183ee678d7a4 100644 --- a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr +++ b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr @@ -5,7 +5,7 @@ LL | let x: &dyn Foo = &(); | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/span-bug-issue-121597.rs:4:12 | LL | trait Foo: for Bar {} @@ -21,7 +21,7 @@ LL | let x: &dyn Foo = &(); | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/span-bug-issue-121597.rs:4:12 | LL | trait Foo: for Bar {} diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr index 4abd7bcf31c5..2869702d7fc6 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr @@ -5,7 +5,7 @@ LL | fn car() -> dyn DynIncompatible { | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { @@ -33,7 +33,7 @@ LL | fn cat() -> Box { | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { @@ -78,7 +78,7 @@ LL | return Box::new(A); | ^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { @@ -107,7 +107,7 @@ LL | Box::new(B) | ^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { diff --git a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr index 44ca09150fe3..61fe9432a1e9 100644 --- a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr +++ b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr @@ -15,7 +15,7 @@ LL | MyTrait::foo(&self) | ^^^^^^^^^^^^ `MyTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:5:22 | LL | trait MyTrait { @@ -40,7 +40,7 @@ LL | impl dyn MyTrait { | ^^^^^^^^^^^ `MyTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:5:22 | LL | trait MyTrait { @@ -57,7 +57,7 @@ LL | fn other(&self) -> impl Marker { | ^^^^ `MyTrait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:5:22 | LL | trait MyTrait { diff --git a/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr b/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr index 87a5480b1e3d..840c27e183f0 100644 --- a/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr +++ b/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr @@ -5,7 +5,7 @@ LL | let i = Box::new(42_u32) as Box; | ^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { @@ -22,7 +22,7 @@ LL | let s = i.baz(); | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { @@ -39,7 +39,7 @@ LL | let s = i.baz(); | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { @@ -56,7 +56,7 @@ LL | let i = Box::new(42_u32) as Box; | ^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { diff --git a/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr b/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr index 07d09468b046..29235ca78a50 100644 --- a/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr +++ b/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr @@ -5,7 +5,7 @@ LL | let _: &dyn rpitit::Foo = todo!(); | ^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/auxiliary/rpitit.rs:4:21 | LL | fn bar(self) -> impl Deref; diff --git a/tests/ui/issues/issue-18959.stderr b/tests/ui/issues/issue-18959.stderr index 49d501c397f1..c37c4177bfc1 100644 --- a/tests/ui/issues/issue-18959.stderr +++ b/tests/ui/issues/issue-18959.stderr @@ -5,7 +5,7 @@ LL | fn foo(b: &dyn Bar) { | ^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } @@ -21,7 +21,7 @@ LL | b.foo(&0) | ^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } @@ -37,7 +37,7 @@ LL | let test: &dyn Bar = &mut thing; | ^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } @@ -53,7 +53,7 @@ LL | let test: &dyn Bar = &mut thing; | ^^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } @@ -70,7 +70,7 @@ LL | foo(test); | ^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } diff --git a/tests/ui/issues/issue-19380.stderr b/tests/ui/issues/issue-19380.stderr index 7d4812c36935..f8509891d3ab 100644 --- a/tests/ui/issues/issue-19380.stderr +++ b/tests/ui/issues/issue-19380.stderr @@ -5,7 +5,7 @@ LL | foos: &'static [&'static (dyn Qiz + 'static)] | ^^^^^^^^^^^^^^^^^ `Qiz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-19380.rs:2:6 | LL | trait Qiz { @@ -29,7 +29,7 @@ LL | const BAR : Bar = Bar { foos: &[&FOO]}; | ^^^^ `Qiz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-19380.rs:2:6 | LL | trait Qiz { @@ -54,7 +54,7 @@ LL | const BAR : Bar = Bar { foos: &[&FOO]}; | ^^^^^^^ `Qiz` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-19380.rs:2:6 | LL | trait Qiz { diff --git a/tests/ui/issues/issue-26056.stderr b/tests/ui/issues/issue-26056.stderr index d1cdf43351ec..c2168af94969 100644 --- a/tests/ui/issues/issue-26056.stderr +++ b/tests/ui/issues/issue-26056.stderr @@ -5,7 +5,7 @@ LL | as &dyn Map; | ^^^^^^^^^^^^^^^^^^^^^^^^^ `Map` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-26056.rs:9:12 | LL | trait Map: MapLookup<::Key> { diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/issues/issue-50781.stderr index 293e9839944a..88b83a83e0cf 100644 --- a/tests/ui/issues/issue-50781.stderr +++ b/tests/ui/issues/issue-50781.stderr @@ -5,7 +5,7 @@ LL | impl Trait for dyn X {} | ^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-50781.rs:4:8 | LL | trait X { @@ -22,7 +22,7 @@ LL | ::foo(&()); | ^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-50781.rs:4:8 | LL | trait X { @@ -40,7 +40,7 @@ LL | ::foo(&()); | ^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-50781.rs:4:8 | LL | trait X { diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr b/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr index 83446fc9ec0f..95048c4454b3 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr +++ b/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr @@ -26,7 +26,7 @@ LL | let z = &x as &dyn Foo; | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/kindck-inherited-copy-bound.rs:10:13 | LL | trait Foo : Copy { @@ -41,7 +41,7 @@ LL | let z = &x as &dyn Foo; | ^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/kindck-inherited-copy-bound.rs:10:13 | LL | trait Foo : Copy { diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr b/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr index 271e5afb9e7e..296f011193e9 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr @@ -26,7 +26,7 @@ LL | let z = &x as &dyn Foo; | ^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/kindck-inherited-copy-bound.rs:10:13 | LL | trait Foo : Copy { diff --git a/tests/ui/resolve/issue-3907-2.stderr b/tests/ui/resolve/issue-3907-2.stderr index 4ab72a42eb84..40cdfb7a3025 100644 --- a/tests/ui/resolve/issue-3907-2.stderr +++ b/tests/ui/resolve/issue-3907-2.stderr @@ -5,7 +5,7 @@ LL | fn bar(_x: Foo) {} | ^^^ `issue_3907::Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/auxiliary/issue-3907.rs:2:8 | LL | fn bar(); diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr b/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr index 3e018995ba55..8382b4867e22 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr @@ -8,7 +8,7 @@ LL | let x = Rc::new(5usize) as Rc; | ^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/arbitrary-self-types-dyn-incompatible.rs:8:18 | LL | trait Foo { @@ -27,7 +27,7 @@ LL | let x = Rc::new(5usize) as Rc; | ^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/arbitrary-self-types-dyn-incompatible.rs:8:18 | LL | trait Foo { diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr b/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr index 12c93d58537e..d324f4641cf2 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr @@ -8,7 +8,7 @@ LL | let x = Rc::new(5usize) as Rc; | ^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/arbitrary-self-types-dyn-incompatible.rs:8:18 | LL | trait Foo { diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr index 08c744979f5c..28427161e870 100644 --- a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr @@ -5,7 +5,7 @@ LL | pub desc: &'static dyn Qux, | ^^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 | LL | trait Qux { @@ -44,7 +44,7 @@ LL | static FOO: &Lint = &Lint { desc: "desc" }; | ^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 | LL | trait Qux { @@ -68,7 +68,7 @@ LL | static FOO: &Lint = &Lint { desc: "desc" }; | ^^^^^^ `Qux` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 | LL | trait Qux { diff --git a/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr b/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr index cb0e7fce9104..ae009d260299 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr @@ -5,7 +5,7 @@ LL | fn bar(x: &dyn Trait) {} | ^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-references-self.rs:2:22 | LL | trait Trait { @@ -25,7 +25,7 @@ LL | fn foo(x: &dyn Other) {} | ^^^^^^^^^ `Other` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-references-self.rs:11:14 | LL | trait Other: Sized {} diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr index 2efcad1e7bd3..742011ad0c0e 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr @@ -18,7 +18,7 @@ LL | fn f(a: dyn A) -> dyn A; | ^^^^^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:3:10 | LL | trait A: Sized { @@ -46,7 +46,7 @@ LL | fn f(a: dyn B) -> dyn B; | ^^^^^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:8 | LL | trait B { diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr index ecb3ee9185f9..843c139851de 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr @@ -18,7 +18,7 @@ LL | fn f(a: A) -> A; | ^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self.rs:2:10 | LL | trait A: Sized { @@ -46,7 +46,7 @@ LL | fn f(a: B) -> B; | ^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self.rs:8:8 | LL | trait B { diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr index 696840d3ba42..e2250807603b 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr @@ -5,7 +5,7 @@ LL | fn bar(x: &dyn Trait) {} | ^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-where-sized.rs:5:8 | LL | trait Trait { diff --git a/tests/ui/suggestions/issue-116434-2015.stderr b/tests/ui/suggestions/issue-116434-2015.stderr index 508c3ec5e4fd..e7b8cd2f101d 100644 --- a/tests/ui/suggestions/issue-116434-2015.stderr +++ b/tests/ui/suggestions/issue-116434-2015.stderr @@ -47,7 +47,7 @@ LL | fn foo() -> Clone; | = note: the trait is not dyn compatible because it requires `Self: Sized` = note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit help: there is an associated type with the same name | LL | fn foo() -> Self::Clone; @@ -74,7 +74,7 @@ LL | fn handle() -> DbHandle; | ^^^^^^^^ `DbHandle` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-116434-2015.rs:14:17 | LL | trait DbHandle: Sized {} diff --git a/tests/ui/suggestions/issue-98500.stderr b/tests/ui/suggestions/issue-98500.stderr index 97b712acfcbe..ec984c0d60e1 100644 --- a/tests/ui/suggestions/issue-98500.stderr +++ b/tests/ui/suggestions/issue-98500.stderr @@ -5,7 +5,7 @@ LL | struct S(Box); | ^^^^^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/auxiliary/dyn-incompatible.rs:4:8 | LL | fn f(); diff --git a/tests/ui/traits/alias/generic-default-in-dyn.stderr b/tests/ui/traits/alias/generic-default-in-dyn.stderr index 1ab9e6d5c5ca..c6f8ab4137b2 100644 --- a/tests/ui/traits/alias/generic-default-in-dyn.stderr +++ b/tests/ui/traits/alias/generic-default-in-dyn.stderr @@ -15,7 +15,7 @@ LL | struct Foo(dyn SendEqAlias); | ^^^^^^^^^^^^^^ `SendEqAlias` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generic-default-in-dyn.rs:1:24 | LL | trait SendEqAlias = PartialEq; @@ -30,7 +30,7 @@ LL | struct Bar(dyn SendEqAlias, T); | ^^^^^^^^^^^^^^ `SendEqAlias` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/generic-default-in-dyn.rs:1:24 | LL | trait SendEqAlias = PartialEq; diff --git a/tests/ui/traits/alias/object-fail.stderr b/tests/ui/traits/alias/object-fail.stderr index 52ce79a45973..d60d88434077 100644 --- a/tests/ui/traits/alias/object-fail.stderr +++ b/tests/ui/traits/alias/object-fail.stderr @@ -5,7 +5,7 @@ LL | let _: &dyn EqAlias = &123; | ^^^^^^^ `EqAlias` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | = note: ...because it uses `Self` as a type parameter diff --git a/tests/ui/traits/alias/self-in-const-generics.stderr b/tests/ui/traits/alias/self-in-const-generics.stderr index 3c799492591a..b5538cb6e2f3 100644 --- a/tests/ui/traits/alias/self-in-const-generics.stderr +++ b/tests/ui/traits/alias/self-in-const-generics.stderr @@ -5,7 +5,7 @@ LL | fn foo(x: &dyn BB) {} | ^^ `BB` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/self-in-const-generics.rs:7:12 | LL | trait BB = Bar<{ 2 + 1 }>; diff --git a/tests/ui/traits/alias/self-in-generics.stderr b/tests/ui/traits/alias/self-in-generics.stderr index 5639b2b44a18..afe4dff45ed6 100644 --- a/tests/ui/traits/alias/self-in-generics.stderr +++ b/tests/ui/traits/alias/self-in-generics.stderr @@ -5,7 +5,7 @@ LL | pub fn f(_f: &dyn SelfInput) {} | ^^^^^^^^^ `SelfInput` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/self-in-generics.rs:6:23 | LL | pub trait SelfInput = Fn(&mut Self); diff --git a/tests/ui/traits/issue-20692.stderr b/tests/ui/traits/issue-20692.stderr index 50ea7cde9611..32e29de49a11 100644 --- a/tests/ui/traits/issue-20692.stderr +++ b/tests/ui/traits/issue-20692.stderr @@ -5,7 +5,7 @@ LL | &dyn Array; | ^^^^^^^^^^ `Array` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-20692.rs:1:14 | LL | trait Array: Sized + Copy {} @@ -21,7 +21,7 @@ LL | let _ = x | ^ `Array` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-20692.rs:1:14 | LL | trait Array: Sized + Copy {} diff --git a/tests/ui/traits/issue-28576.stderr b/tests/ui/traits/issue-28576.stderr index ba113d573d69..ba61ce985544 100644 --- a/tests/ui/traits/issue-28576.stderr +++ b/tests/ui/traits/issue-28576.stderr @@ -27,7 +27,7 @@ LL | | | |________________________^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-28576.rs:5:16 | LL | pub trait Bar: Foo { diff --git a/tests/ui/traits/issue-38404.stderr b/tests/ui/traits/issue-38404.stderr index f9e592255dd7..63269d3379ed 100644 --- a/tests/ui/traits/issue-38404.stderr +++ b/tests/ui/traits/issue-38404.stderr @@ -5,7 +5,7 @@ LL | trait C: A> {} | ^^^^^^^^^^^^^^^^^^^^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-38404.rs:1:13 | LL | trait A: std::ops::Add + Sized {} @@ -20,7 +20,7 @@ LL | trait C: A> {} | ^^^^^^^^^^^^^^^^^^^^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-38404.rs:1:13 | LL | trait A: std::ops::Add + Sized {} @@ -36,7 +36,7 @@ LL | trait C: A> {} | ^^^^^^^^^^^^^^^^^^^^ `B` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-38404.rs:1:13 | LL | trait A: std::ops::Add + Sized {} diff --git a/tests/ui/traits/issue-38604.stderr b/tests/ui/traits/issue-38604.stderr index 94f9c1540ad3..e6a6b44e7304 100644 --- a/tests/ui/traits/issue-38604.stderr +++ b/tests/ui/traits/issue-38604.stderr @@ -5,7 +5,7 @@ LL | let _f: Box = | ^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-38604.rs:2:22 | LL | trait Foo where u32: Q { @@ -21,7 +21,7 @@ LL | Box::new(()); | ^^^^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-38604.rs:2:22 | LL | trait Foo where u32: Q { diff --git a/tests/ui/traits/issue-72410.stderr b/tests/ui/traits/issue-72410.stderr index 002345bff84d..c9e133437dd8 100644 --- a/tests/ui/traits/issue-72410.stderr +++ b/tests/ui/traits/issue-72410.stderr @@ -5,7 +5,7 @@ LL | where for<'a> &'a mut [dyn Bar]: ; | ^^^^^^^^^^^^^^^^^ `Bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-72410.rs:13:8 | LL | pub trait Bar { diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index c97158a5b760..58c558d66852 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -143,7 +143,7 @@ LL | ::A; | ^^^^^ `assoc_const::C` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/item-privacy.rs:25:15 | LL | const A: u8 = 0; diff --git a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr index 682d18842b88..c8a1329e3d0f 100644 --- a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr +++ b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr @@ -41,7 +41,7 @@ LL | impl Foo { | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/missing-for-type-in-impl.rs:4:8 | LL | trait Foo { diff --git a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr index 8448890c0847..43b69d0b50e4 100644 --- a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr +++ b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr @@ -14,7 +14,7 @@ LL | let x: &dyn Foo = &(); | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/supertrait-dyn-compatibility.rs:4:12 | LL | trait Foo: for Bar {} @@ -31,7 +31,7 @@ LL | let x: &dyn Foo = &(); | ^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/supertrait-dyn-compatibility.rs:4:12 | LL | trait Foo: for Bar {} @@ -47,7 +47,7 @@ LL | needs_bar(x); | ^^^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/supertrait-dyn-compatibility.rs:4:12 | LL | trait Foo: for Bar {} diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr index ae3762704c6a..b4bbd65b2f47 100644 --- a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -20,7 +20,7 @@ LL | let b: &dyn FromResidual = &(); | ^^^ `FromResidual` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 | LL | trait FromResidual::Residual> { @@ -44,7 +44,7 @@ LL | let b: &dyn FromResidual = &(); | ^^^^^^^^^^^^^^^^^ `FromResidual` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 | LL | trait FromResidual::Residual> { diff --git a/tests/ui/traits/object/macro-matcher.stderr b/tests/ui/traits/object/macro-matcher.stderr index ab0fc213c9f1..3c668ce99c7f 100644 --- a/tests/ui/traits/object/macro-matcher.stderr +++ b/tests/ui/traits/object/macro-matcher.stderr @@ -12,7 +12,7 @@ LL | m!(dyn Copy + Send + 'static); | = note: the trait is not dyn compatible because it requires `Self: Sized` = note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit error: aborting due to 2 previous errors diff --git a/tests/ui/traits/object/safety.stderr b/tests/ui/traits/object/safety.stderr index eab59f39c284..593e42619f41 100644 --- a/tests/ui/traits/object/safety.stderr +++ b/tests/ui/traits/object/safety.stderr @@ -5,7 +5,7 @@ LL | let _: &dyn Tr = &St; | ^^^ `Tr` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/safety.rs:4:8 | LL | trait Tr { @@ -30,7 +30,7 @@ LL | let _: &dyn Tr = &St; | ^^^^^^^ `Tr` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/safety.rs:4:8 | LL | trait Tr { diff --git a/tests/ui/traits/test-2.stderr b/tests/ui/traits/test-2.stderr index 8915e490b4d2..6a6cb503aa4d 100644 --- a/tests/ui/traits/test-2.stderr +++ b/tests/ui/traits/test-2.stderr @@ -33,7 +33,7 @@ LL | (Box::new(10) as Box).dup(); | ^^^^^^^^^^^^ `bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/test-2.rs:4:30 | LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } @@ -56,7 +56,7 @@ LL | (Box::new(10) as Box).dup(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/test-2.rs:4:30 | LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } @@ -79,7 +79,7 @@ LL | (Box::new(10) as Box).dup(); | ^^^^^^^^^^^^ `bar` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/test-2.rs:4:30 | LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } diff --git a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr index 71717c6945e0..eea2e75a2382 100644 --- a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr +++ b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr @@ -17,7 +17,7 @@ LL | let y = x as dyn MyAdd; | ^^^^^^^^^^^^^^ `MyAdd` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:6:55 | LL | trait MyAdd { fn add(&self, other: &Rhs) -> Self; } diff --git a/tests/ui/wf/issue-87495.stderr b/tests/ui/wf/issue-87495.stderr index 7be327e61d10..0c293e3576d6 100644 --- a/tests/ui/wf/issue-87495.stderr +++ b/tests/ui/wf/issue-87495.stderr @@ -5,7 +5,7 @@ LL | const CONST: (bool, dyn T); | ^^^^^ `T` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/issue-87495.rs:4:11 | LL | trait T { diff --git a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr index 0b7f4cd43622..f3e4f2a63e98 100644 --- a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr +++ b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr @@ -5,7 +5,7 @@ LL | let t_box: Box = Box::new(S); | ^^^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:6:14 | LL | trait Trait: Sized {} @@ -22,7 +22,7 @@ LL | takes_box(Box::new(S)); | ^^^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:6:14 | LL | trait Trait: Sized {} @@ -39,7 +39,7 @@ LL | Box::new(S) as Box; | ^^^^^^^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:6:14 | LL | trait Trait: Sized {} diff --git a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr index 3f50e1192cf0..716d0e78ff11 100644 --- a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr +++ b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr @@ -5,7 +5,7 @@ LL | let t: &dyn Trait = &S; | ^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:6:14 | LL | trait Trait: Sized {} @@ -22,7 +22,7 @@ LL | takes_trait(&S); | ^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:6:14 | LL | trait Trait: Sized {} @@ -39,7 +39,7 @@ LL | &S as &dyn Trait; | ^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:6:14 | LL | trait Trait: Sized {} diff --git a/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr b/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr index 8f68f9c5b6b3..a7405ce4caa9 100644 --- a/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr +++ b/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr @@ -19,7 +19,7 @@ LL | Some(()) => &S, | ^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-dyn-incompat-trait-obj-match.rs:6:14 | LL | trait Trait: Sized {} @@ -40,7 +40,7 @@ LL | None => &R, | ^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-dyn-incompat-trait-obj-match.rs:6:14 | LL | trait Trait: Sized {} diff --git a/tests/ui/wf/wf-dyn-incompatible.stderr b/tests/ui/wf/wf-dyn-incompatible.stderr index 1803376aaa19..e61b37d92932 100644 --- a/tests/ui/wf/wf-dyn-incompatible.stderr +++ b/tests/ui/wf/wf-dyn-incompatible.stderr @@ -5,7 +5,7 @@ LL | let _x: &dyn A; | ^^^^^^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit --> $DIR/wf-dyn-incompatible.rs:5:23 | LL | trait A { diff --git a/tests/ui/wf/wf-fn-where-clause.stderr b/tests/ui/wf/wf-fn-where-clause.stderr index d73376e9861e..b419bc8347fb 100644 --- a/tests/ui/wf/wf-fn-where-clause.stderr +++ b/tests/ui/wf/wf-fn-where-clause.stderr @@ -22,7 +22,7 @@ LL | fn bar() where Vec:, {} | = note: the trait is not dyn compatible because it requires `Self: Sized` = note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit + for more information, visit error[E0277]: the size for values of type `(dyn Copy + 'static)` cannot be known at compilation time --> $DIR/wf-fn-where-clause.rs:12:16 From 1ee75825455328c287cb2ad1bdbd855656f09871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 17:18:07 +0000 Subject: [PATCH 164/342] add NLL region graph to the polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 84 +++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 40e801d03885..11b00fdd44aa 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,14 +1,17 @@ use std::io; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; use rustc_middle::mir::{Body, ClosureRegionRequirements}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. @@ -50,6 +53,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the NLL MIR /// - the list of polonius localized constraints /// - a mermaid graph of the CFG +/// - a mermaid graph of the NLL regions and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -89,6 +93,14 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "")?; writeln!(out, "")?; + // Section 3: mermaid visualization of the NLL region graph. + writeln!(out, "
")?; + writeln!(out, "NLL regions")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_regions(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + // Finalize the dump with the HTML epilogue. writeln!( out, @@ -261,3 +273,73 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> Ok(()) } + +/// Emits a region's label: index, universe, external name. +fn render_region( + region: RegionVid, + regioncx: &RegionInferenceContext<'_>, + out: &mut dyn io::Write, +) -> io::Result<()> { + let def = regioncx.region_definition(region); + let universe = def.universe; + + write!(out, "'{}", region.as_usize())?; + if !universe.is_root() { + write!(out, "/{universe:?}")?; + } + if let Some(name) = def.external_name.and_then(|e| e.get_name()) { + write!(out, " ({name})")?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_regions<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the region nodes. + for region in regioncx.var_infos.indices() { + write!(out, "{}[\"", region.as_usize())?; + render_region(region, regioncx, out)?; + writeln!(out, "\"]")?; + } + + // Get a set of edges to check for the reverse edge being present. + let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); + + // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. + let constraint_key = |c: &OutlivesConstraint<'_>| { + let min = c.sup.min(c.sub); + let max = c.sup.max(c.sub); + (min, max) + }; + let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); + ordered_edges.sort_by_key(|c| constraint_key(c)); + ordered_edges.dedup_by_key(|c| constraint_key(c)); + + for outlives in ordered_edges { + // Source node. + write!(out, "{} ", outlives.sup.as_usize())?; + + // The kind of arrow: bidirectional if the opposite edge exists in the set. + if edges.contains(&(outlives.sub, outlives.sup)) { + write!(out, "<")?; + } + write!(out, "-- ")?; + + // Edge label from its `Locations`. + match outlives.locations { + Locations::All(_) => write!(out, "All")?, + Locations::Single(location) => write!(out, "{:?}", location)?, + } + + // Target node. + writeln!(out, " --> {}", outlives.sub.as_usize())?; + } + Ok(()) +} From 052e9b430651f5cb89b511d131d1d7a47a28d215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 21:13:32 +0000 Subject: [PATCH 165/342] add NLL SCCs to polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 11b00fdd44aa..944b5b1d4a7d 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,6 +1,7 @@ use std::io; use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; @@ -54,6 +55,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the list of polonius localized constraints /// - a mermaid graph of the CFG /// - a mermaid graph of the NLL regions and the constraints between them +/// - a mermaid graph of the NLL SCCs and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -101,6 +103,14 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "")?; writeln!(out, "")?; + // Section 4: mermaid visualization of the NLL SCC graph. + writeln!(out, "
")?; + writeln!(out, "NLL SCCs")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + // Finalize the dump with the HTML epilogue. writeln!( out, @@ -343,3 +353,42 @@ fn emit_mermaid_nll_regions<'tcx>( } Ok(()) } + +/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_sccs<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Gather and emit the SCC nodes. + let mut nodes_per_scc: IndexVec<_, _> = + regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); + for region in regioncx.var_infos.indices() { + let scc = regioncx.constraint_sccs().scc(region); + nodes_per_scc[scc].push(region); + } + for (scc, regions) in nodes_per_scc.iter_enumerated() { + // The node label: the regions contained in the SCC. + write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; + for (idx, ®ion) in regions.iter().enumerate() { + render_region(region, regioncx, out)?; + if idx < regions.len() - 1 { + write!(out, ",")?; + } + } + writeln!(out, "}}\"]")?; + } + + // Emit the edges between SCCs. + let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { + regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + }); + for (source, target) in edges { + writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; + } + + Ok(()) +} From 6bdc2dc3cf7534a8066a8f937698f4a7520b85aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 21:22:46 +0000 Subject: [PATCH 166/342] tidy up html structure - invert pre/code which was an invalid combination, that works fine in practice - remove unneeded code wrapper for graphs --- compiler/rustc_borrowck/src/polonius/dump.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 944b5b1d4a7d..f71e6f3e6f3a 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -74,7 +74,7 @@ fn emit_polonius_dump<'tcx>( // Section 1: the NLL + Polonius MIR. writeln!(out, "
")?; writeln!(out, "Raw MIR dump")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_html_mir(
         tcx,
         body,
@@ -84,15 +84,15 @@ fn emit_polonius_dump<'tcx>(
         closure_region_requirements,
         out,
     )?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 2: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_mermaid_cfg(body, out)?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 3: mermaid visualization of the NLL region graph. From 395f0c9ecd1c3f238707c7a0788e4b1d84e2e70e Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 12 Jan 2025 20:18:45 +0000 Subject: [PATCH 167/342] Stabilize `const_black_box` This has been unstably const since [1], but a tracking issue was never created. Per discussion on Zulip [2], there should not be any blockers to making this const-stable. The function does not provide any functionality at compile time but does allow code reuse between const- and non-const functions, so stabilize it here. [1]: https://github.com/rust-lang/rust/pull/92226 [2]: https://rust-lang.zulipchat.com/#narrow/channel/146212-t-compiler.2Fconst-eval/topic/const_black_box --- compiler/rustc_codegen_gcc/tests/run/int.rs | 2 -- library/core/src/hint.rs | 4 +++- library/core/src/intrinsics/mod.rs | 1 + library/coretests/tests/lib.rs | 1 - 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_gcc/tests/run/int.rs b/compiler/rustc_codegen_gcc/tests/run/int.rs index bfe73c38435a..58a26801b678 100644 --- a/compiler/rustc_codegen_gcc/tests/run/int.rs +++ b/compiler/rustc_codegen_gcc/tests/run/int.rs @@ -3,8 +3,6 @@ // Run-time: // status: 0 -#![feature(const_black_box)] - /* * Code */ diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 80f6e32b6b2c..e5c1a64c12ee 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -468,9 +468,11 @@ pub fn spin_loop() { /// // No assumptions can be made about either operand, so the multiplication is not optimized out. /// let y = black_box(5) * black_box(10); /// ``` +/// +/// During constant evaluation, `black_box` is treated as a no-op. #[inline] #[stable(feature = "bench_black_box", since = "1.66.0")] -#[rustc_const_unstable(feature = "const_black_box", issue = "none")] +#[rustc_const_stable(feature = "const_black_box", since = "CURRENT_RUSTC_VERSION")] pub const fn black_box(dummy: T) -> T { crate::intrinsics::black_box(dummy) } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 41b2ffad6680..c0d435f99c0c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3725,6 +3725,7 @@ pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: u #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] +#[rustc_intrinsic_const_stable_indirect] pub const fn black_box(_dummy: T) -> T { unimplemented!() } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0607d508a48e..7fe728626085 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -14,7 +14,6 @@ #![feature(bstr)] #![feature(cell_update)] #![feature(clone_to_uninit)] -#![feature(const_black_box)] #![feature(const_eval_select)] #![feature(const_swap_nonoverlapping)] #![feature(const_trait_impl)] From a9213c27ad09a97ac769c451bbed78e91c84cab8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 10:17:03 +0000 Subject: [PATCH 168/342] Deduplicate operand creation between scalars, non-scalars and string patterns --- .../src/builder/matches/test.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 8cca84d7fcc6..ec2a93001360 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -145,6 +145,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); + + let expect_ty = value.ty(); + let expect = self.literal_operand(test.span, value); if let ty::Adt(def, _) = ty.kind() && tcx.is_lang_item(def.did(), LangItem::String) { @@ -173,7 +176,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block, fail_block, source_info, - value, + expect, + expect_ty, ref_str, ref_str_ty, ); @@ -185,13 +189,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block, fail_block, source_info, - value, + expect, + expect_ty, place, ty, ); } else { - assert_eq!(value.ty(), ty); - let expect = self.literal_operand(test.span, value); + assert_eq!(expect_ty, ty); let val = Operand::Copy(place); self.compare( block, @@ -371,12 +375,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block: BasicBlock, fail_block: BasicBlock, source_info: SourceInfo, - value: Const<'tcx>, + mut expect: Operand<'tcx>, + expect_ty: Ty<'tcx>, mut val: Place<'tcx>, mut ty: Ty<'tcx>, ) { - let mut expect = self.literal_operand(source_info.span, value); - // If we're using `b"..."` as a pattern, we need to insert an // unsizing coercion, as the byte string has the type `&[u8; N]`. // @@ -391,7 +394,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => None, }; let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty()); + let opt_ref_test_ty = unsize(expect_ty); match (opt_ref_ty, opt_ref_test_ty) { // nothing to do, neither is an array (None, None) => {} From e1e2e17d2059a0c5e4d1770d1db30d9cf7bb4b26 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 10:35:03 +0000 Subject: [PATCH 169/342] Use an operand instead of a place that is always turned into an operand --- .../src/builder/matches/test.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index ec2a93001360..f7b0f734b2de 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -178,7 +178,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, expect, expect_ty, - ref_str, + Operand::Copy(ref_str), ref_str_ty, ); } else if !ty.is_scalar() { @@ -191,12 +191,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, expect, expect_ty, - place, + Operand::Copy(place), ty, ); } else { assert_eq!(expect_ty, ty); - let val = Operand::Copy(place); self.compare( block, success_block, @@ -204,7 +203,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, BinOp::Eq, expect, - val, + Operand::Copy(place), ); } } @@ -377,7 +376,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, mut expect: Operand<'tcx>, expect_ty: Ty<'tcx>, - mut val: Place<'tcx>, + mut val: Operand<'tcx>, mut ty: Ty<'tcx>, ) { // If we're using `b"..."` as a pattern, we need to insert an @@ -413,11 +412,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PointerCoercion::Unsize, CoercionSource::Implicit, ), - Operand::Copy(val), + val, ty, ), ); - val = temp; + val = Operand::Copy(temp); } if opt_ref_test_ty.is_some() { let slice = self.temp(ty, source_info.span); @@ -473,11 +472,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { const_: method, })), - args: [Spanned { node: Operand::Copy(val), span: DUMMY_SP }, Spanned { - node: expect, - span: DUMMY_SP, - }] - .into(), + args: [Spanned { node: val, span: DUMMY_SP }, Spanned { node: expect, span: DUMMY_SP }] + .into(), destination: eq_result, target: Some(eq_block), unwind: UnwindAction::Continue, From d898aa3c334efacb64295f1dd36cfd50e0a74e9d Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Mon, 27 Jan 2025 09:59:01 +0000 Subject: [PATCH 170/342] Arbitrary self types v2: explain test. The purpose of this test wasn't obvious. Add a comment. --- ...arbitrary_self_types_recursive_receiver.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/ui/self/arbitrary_self_types_recursive_receiver.rs b/tests/ui/self/arbitrary_self_types_recursive_receiver.rs index f3e7f96d7c4d..8b1b6a8a105f 100644 --- a/tests/ui/self/arbitrary_self_types_recursive_receiver.rs +++ b/tests/ui/self/arbitrary_self_types_recursive_receiver.rs @@ -1,6 +1,22 @@ //@ run-pass #![feature(arbitrary_self_types)] +// When probing for methods, we step forward through a chain of types. The first +// few of those steps can be reached by jumping through the chain of Derefs or the +// chain of Receivers. Later steps can only be reached by following the chain of +// Receivers. For instance, supposing A and B implement both Receiver and Deref, +// while C and D implement only Receiver: +// +// Type A>>> +// +// Deref chain: A -> B -> C +// Receiver chain: A -> B -> C -> D -> E +// +// We report bad type errors from the end of the chain. But at the end of which +// chain? We never morph the type as far as E so the correct behavior is to +// report errors from point C, i.e. the end of the Deref chain. This test case +// ensures we do that. + struct MyNonNull(*const T); impl std::ops::Receiver for MyNonNull { @@ -10,7 +26,13 @@ impl std::ops::Receiver for MyNonNull { #[allow(dead_code)] impl MyNonNull { fn foo(&self) -> *const U { - self.cast::().bar() + let mnn = self.cast::(); + // The following method call is the point of this test. + // If probe.rs reported errors from the last type discovered + // in the Receiver chain, it would be sad here because U is just + // a type variable. But this is a valid call so it ensures + // probe.rs doesn't make that mistake. + mnn.bar() } fn cast(&self) -> MyNonNull { MyNonNull(self.0 as *const U) From f895e31d590d5c10e8cdb19e042f631e435c683f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 27 Jan 2025 15:10:42 +0100 Subject: [PATCH 171/342] Fix SIMD codegen tests on LLVM 20 The splat contents are printed differently on LLVM 20. --- .../simd-intrinsic/simd-intrinsic-generic-gather.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-masked-load.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-masked-store.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-scatter.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-select.rs | 4 ++-- .../codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs | 8 ++++---- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs index 605a0d520a77..7f99f695bf4f 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs @@ -23,7 +23,7 @@ extern "rust-intrinsic" { #[no_mangle] pub unsafe fn gather_f32x2(pointers: Vec2<*const f32>, mask: Vec2, values: Vec2) -> Vec2 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}}) simd_gather(values, pointers, mask) @@ -33,7 +33,7 @@ pub unsafe fn gather_f32x2(pointers: Vec2<*const f32>, mask: Vec2, #[no_mangle] pub unsafe fn gather_pf32x2(pointers: Vec2<*const *const f32>, mask: Vec2, values: Vec2<*const f32>) -> Vec2<*const f32> { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call <2 x ptr> @llvm.masked.gather.v2p0.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x ptr> {{.*}}) simd_gather(values, pointers, mask) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs index 015f6fd9cef4..7f46630e920d 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs @@ -21,7 +21,7 @@ extern "rust-intrinsic" { #[no_mangle] pub unsafe fn load_f32x2(mask: Vec2, pointer: *const f32, values: Vec2) -> Vec2 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 4, <2 x i1> [[B]], <2 x float> {{.*}}) simd_masked_load(mask, pointer, values) @@ -31,7 +31,7 @@ pub unsafe fn load_f32x2(mask: Vec2, pointer: *const f32, #[no_mangle] pub unsafe fn load_pf32x4(mask: Vec4, pointer: *const *const f32, values: Vec4<*const f32>) -> Vec4<*const f32> { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1> // CHECK: call <4 x ptr> @llvm.masked.load.v4p0.p0(ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]], <4 x ptr> {{.*}}) simd_masked_load(mask, pointer, values) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs index 471a4bea181b..0d43234f1e29 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs @@ -20,7 +20,7 @@ extern "rust-intrinsic" { // CHECK-LABEL: @store_f32x2 #[no_mangle] pub unsafe fn store_f32x2(mask: Vec2, pointer: *mut f32, values: Vec2) { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr {{.*}}, i32 4, <2 x i1> [[B]]) simd_masked_store(mask, pointer, values) @@ -29,7 +29,7 @@ pub unsafe fn store_f32x2(mask: Vec2, pointer: *mut f32, values: Vec2) // CHECK-LABEL: @store_pf32x4 #[no_mangle] pub unsafe fn store_pf32x4(mask: Vec4, pointer: *mut *const f32, values: Vec4<*const f32>) { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1> // CHECK: call void @llvm.masked.store.v4p0.p0(<4 x ptr> {{.*}}, ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]]) simd_masked_store(mask, pointer, values) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs index 1c42b2534d87..ef7827bd96f0 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs @@ -23,7 +23,7 @@ extern "rust-intrinsic" { #[no_mangle] pub unsafe fn scatter_f32x2(pointers: Vec2<*mut f32>, mask: Vec2, values: Vec2) { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call void @llvm.masked.scatter.v2f32.v2p0(<2 x float> {{.*}}, <2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]] simd_scatter(values, pointers, mask) @@ -34,7 +34,7 @@ pub unsafe fn scatter_f32x2(pointers: Vec2<*mut f32>, mask: Vec2, #[no_mangle] pub unsafe fn scatter_pf32x2(pointers: Vec2<*mut *const f32>, mask: Vec2, values: Vec2<*const f32>) { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call void @llvm.masked.scatter.v2p0.v2p0(<2 x ptr> {{.*}}, <2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]] simd_scatter(values, pointers, mask) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs index a73593160f2e..33ed2b437f9b 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs @@ -29,7 +29,7 @@ extern "rust-intrinsic" { // CHECK-LABEL: @select_m8 #[no_mangle] pub unsafe fn select_m8(m: b8x4, a: f32x4, b: f32x4) -> f32x4 { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i8> %{{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i8> %{{.*}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i8> [[A]] to <4 x i1> // CHECK: select <4 x i1> [[B]] simd_select(m, a, b) @@ -38,7 +38,7 @@ pub unsafe fn select_m8(m: b8x4, a: f32x4, b: f32x4) -> f32x4 { // CHECK-LABEL: @select_m32 #[no_mangle] pub unsafe fn select_m32(m: i32x4, a: f32x4, b: f32x4) -> f32x4 { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> %{{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> %{{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1> // CHECK: select <4 x i1> [[B]] simd_select(m, a, b) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs index 4df246c2f5c7..92067db9b153 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs @@ -27,7 +27,7 @@ extern "rust-intrinsic" { // CHECK-LABEL: @reduce_any_32x2 #[no_mangle] pub unsafe fn reduce_any_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 @@ -37,7 +37,7 @@ pub unsafe fn reduce_any_32x2(x: mask32x2) -> bool { // CHECK-LABEL: @reduce_all_32x2 #[no_mangle] pub unsafe fn reduce_all_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 @@ -47,7 +47,7 @@ pub unsafe fn reduce_all_32x2(x: mask32x2) -> bool { // CHECK-LABEL: @reduce_any_8x16 #[no_mangle] pub unsafe fn reduce_any_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v16i1(<16 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 @@ -57,7 +57,7 @@ pub unsafe fn reduce_any_8x16(x: mask8x16) -> bool { // CHECK-LABEL: @reduce_all_8x16 #[no_mangle] pub unsafe fn reduce_all_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v16i1(<16 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 From ff19f2f4c10d9d0a0b27e64aed8dfd8519458c13 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:43:39 +0100 Subject: [PATCH 172/342] ci: use ubuntu 24 on free runners --- src/ci/github-actions/jobs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 7730d29d28f6..f56909343385 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -5,7 +5,7 @@ runners: env: { } - &job-linux-4c - os: ubuntu-22.04 + os: ubuntu-24.04 # Free some disk space to avoid running out of space during the build. free_disk: true <<: *base-job @@ -50,7 +50,7 @@ runners: - &job-aarch64-linux # Free some disk space to avoid running out of space during the build. free_disk: true - os: ubuntu-22.04-arm + os: ubuntu-24.04-arm - &job-aarch64-linux-8c os: ubuntu-22.04-arm64-8core-32gb From cedd3e22a64ad7a1097414f314cf444dbd115949 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 27 Jan 2025 12:00:59 -0500 Subject: [PATCH 173/342] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book b/src/doc/book index 82a4a49789bc..fa312a343fbf 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 82a4a49789bc96db1a1b2a210b4c5ed7c9ef0c0d +Subproject commit fa312a343fbff01bc6cef393e326817f70719813 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index d56e0f3a0656..4ed5a1a4a2a7 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82 +Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda diff --git a/src/doc/nomicon b/src/doc/nomicon index 625b200e5b33..bc2298865544 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 625b200e5b33a5af35589db0bc454203a3d46d20 +Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948 diff --git a/src/doc/reference b/src/doc/reference index 293af9910037..93b921c7d321 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 293af991003772bdccf2d6b980182d84dd055942 +Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d From 5082fd8b1ee31284be473fe4e8691677ff61c708 Mon Sep 17 00:00:00 2001 From: Tyrone Wu Date: Sun, 17 Nov 2024 21:02:05 +0000 Subject: [PATCH 174/342] Trim extra whitespace in fn ptr suggestion span Trim extra whitespace when suggesting removal of invalid qualifiers when parsing function pointer type. Fixes: #133083 Signed-off-by: Tyrone Wu --- compiler/rustc_parse/src/errors.rs | 6 ++- compiler/rustc_parse/src/parser/ty.rs | 50 +++++++++++++++++-- tests/ui/parser/bad-fn-ptr-qualifier.fixed | 28 +++++------ tests/ui/parser/bad-fn-ptr-qualifier.stderr | 32 ++++++------ .../recover/recover-const-async-fn-ptr.stderr | 32 ++++++------ 5 files changed, 96 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index f78d9dc2bfc2..3d07166274d5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2830,9 +2830,10 @@ pub(crate) struct DynAfterMut { pub(crate) struct FnPointerCannotBeConst { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -2840,9 +2841,10 @@ pub(crate) struct FnPointerCannotBeConst { pub(crate) struct FnPointerCannotBeAsync { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6497d19a173c..dc5919b3630c 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -609,16 +609,58 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, safety, constness, coroutine_kind } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; + let fn_start_lo = self.prev_token.span.lo(); if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); - if let ast::Const::Yes(span) = constness { - self.dcx().emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); + + // Order/parsing of "front matter" follows: + // ` fn()` + // ^ ^ ^ ^ ^ + // | | | | fn_start_lo + // | | | ext_sp.lo + // | | safety_sp.lo + // | coroutine_sp.lo + // const_sp.lo + if let ast::Const::Yes(const_span) = constness { + let next_token_lo = if let Some( + ast::CoroutineKind::Async { span, .. } + | ast::CoroutineKind::Gen { span, .. } + | ast::CoroutineKind::AsyncGen { span, .. }, + ) = coroutine_kind + { + span.lo() + } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = const_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeConst { + span: whole_span, + qualifier: const_span, + suggestion: sugg_span, + }); } - if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind { - self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); + if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind { + let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety + { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = async_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeAsync { + span: whole_span, + qualifier: async_span, + suggestion: sugg_span, + }); } // FIXME(gen_blocks): emit a similar error for `gen fn()` let decl_span = span_start.to(self.prev_token.span); diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.fixed b/tests/ui/parser/bad-fn-ptr-qualifier.fixed index e2a2f9486b78..8a97a2f09cca 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.fixed +++ b/tests/ui/parser/bad-fn-ptr-qualifier.fixed @@ -2,24 +2,24 @@ //@ edition:2018 // Most of items are taken from ./recover-const-async-fn-ptr.rs but this is able to apply rustfix. -pub type T0 = fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T1 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T2 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T3 = fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T4 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T5 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T6 = unsafe extern "C" fn(); +pub type T0 = fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T1 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T2 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T3 = fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T4 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T5 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T6 = unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` //~| ERROR an `fn` pointer type cannot be `async` -pub type FTT0 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT1 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT2 = for<'a> unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT3 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT4 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT5 = for<'a> unsafe extern "C" fn(); +pub type FTT0 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT1 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT2 = for<'a> unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT3 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT4 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT5 = for<'a> unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `async` -pub type FTT6 = for<'a> unsafe extern "C" fn(); +pub type FTT6 = for<'a> unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` //~| ERROR an `fn` pointer type cannot be `async` diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.stderr b/tests/ui/parser/bad-fn-ptr-qualifier.stderr index ddc8bac678cf..b9d2625d9f4b 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.stderr +++ b/tests/ui/parser/bad-fn-ptr-qualifier.stderr @@ -9,7 +9,7 @@ LL | pub type T0 = const fn(); help: remove the `const` qualifier | LL - pub type T0 = const fn(); -LL + pub type T0 = fn(); +LL + pub type T0 = fn(); | error: an `fn` pointer type cannot be `const` @@ -23,7 +23,7 @@ LL | pub type T1 = const extern "C" fn(); help: remove the `const` qualifier | LL - pub type T1 = const extern "C" fn(); -LL + pub type T1 = extern "C" fn(); +LL + pub type T1 = extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -37,7 +37,7 @@ LL | pub type T2 = const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type T2 = const unsafe extern "C" fn(); -LL + pub type T2 = unsafe extern "C" fn(); +LL + pub type T2 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -51,7 +51,7 @@ LL | pub type T3 = async fn(); help: remove the `async` qualifier | LL - pub type T3 = async fn(); -LL + pub type T3 = fn(); +LL + pub type T3 = fn(); | error: an `fn` pointer type cannot be `async` @@ -65,7 +65,7 @@ LL | pub type T4 = async extern "C" fn(); help: remove the `async` qualifier | LL - pub type T4 = async extern "C" fn(); -LL + pub type T4 = extern "C" fn(); +LL + pub type T4 = extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -79,7 +79,7 @@ LL | pub type T5 = async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type T5 = async unsafe extern "C" fn(); -LL + pub type T5 = unsafe extern "C" fn(); +LL + pub type T5 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -93,7 +93,7 @@ LL | pub type T6 = const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type T6 = const async unsafe extern "C" fn(); -LL + pub type T6 = async unsafe extern "C" fn(); +LL + pub type T6 = async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -107,7 +107,7 @@ LL | pub type T6 = const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type T6 = const async unsafe extern "C" fn(); -LL + pub type T6 = const unsafe extern "C" fn(); +LL + pub type T6 = const unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -121,7 +121,7 @@ LL | pub type FTT0 = for<'a> const fn(); help: remove the `const` qualifier | LL - pub type FTT0 = for<'a> const fn(); -LL + pub type FTT0 = for<'a> fn(); +LL + pub type FTT0 = for<'a> fn(); | error: an `fn` pointer type cannot be `const` @@ -135,7 +135,7 @@ LL | pub type FTT1 = for<'a> const extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT1 = for<'a> const extern "C" fn(); -LL + pub type FTT1 = for<'a> extern "C" fn(); +LL + pub type FTT1 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -149,7 +149,7 @@ LL | pub type FTT2 = for<'a> const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT2 = for<'a> const unsafe extern "C" fn(); -LL + pub type FTT2 = for<'a> unsafe extern "C" fn(); +LL + pub type FTT2 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -163,7 +163,7 @@ LL | pub type FTT3 = for<'a> async fn(); help: remove the `async` qualifier | LL - pub type FTT3 = for<'a> async fn(); -LL + pub type FTT3 = for<'a> fn(); +LL + pub type FTT3 = for<'a> fn(); | error: an `fn` pointer type cannot be `async` @@ -177,7 +177,7 @@ LL | pub type FTT4 = for<'a> async extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT4 = for<'a> async extern "C" fn(); -LL + pub type FTT4 = for<'a> extern "C" fn(); +LL + pub type FTT4 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -191,7 +191,7 @@ LL | pub type FTT5 = for<'a> async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT5 = for<'a> async unsafe extern "C" fn(); -LL + pub type FTT5 = for<'a> unsafe extern "C" fn(); +LL + pub type FTT5 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -205,7 +205,7 @@ LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn(); -LL + pub type FTT6 = for<'a> async unsafe extern "C" fn(); +LL + pub type FTT6 = for<'a> async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -219,7 +219,7 @@ LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn(); -LL + pub type FTT6 = for<'a> const unsafe extern "C" fn(); +LL + pub type FTT6 = for<'a> const unsafe extern "C" fn(); | error: aborting due to 16 previous errors diff --git a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr index 9112a0e135a5..4e5927914ccb 100644 --- a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr +++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr @@ -9,7 +9,7 @@ LL | type T0 = const fn(); help: remove the `const` qualifier | LL - type T0 = const fn(); -LL + type T0 = fn(); +LL + type T0 = fn(); | error: an `fn` pointer type cannot be `const` @@ -23,7 +23,7 @@ LL | type T1 = const extern "C" fn(); help: remove the `const` qualifier | LL - type T1 = const extern "C" fn(); -LL + type T1 = extern "C" fn(); +LL + type T1 = extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -37,7 +37,7 @@ LL | type T2 = const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type T2 = const unsafe extern "C" fn(); -LL + type T2 = unsafe extern "C" fn(); +LL + type T2 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -51,7 +51,7 @@ LL | type T3 = async fn(); help: remove the `async` qualifier | LL - type T3 = async fn(); -LL + type T3 = fn(); +LL + type T3 = fn(); | error: an `fn` pointer type cannot be `async` @@ -65,7 +65,7 @@ LL | type T4 = async extern "C" fn(); help: remove the `async` qualifier | LL - type T4 = async extern "C" fn(); -LL + type T4 = extern "C" fn(); +LL + type T4 = extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -79,7 +79,7 @@ LL | type T5 = async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type T5 = async unsafe extern "C" fn(); -LL + type T5 = unsafe extern "C" fn(); +LL + type T5 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -93,7 +93,7 @@ LL | type T6 = const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type T6 = const async unsafe extern "C" fn(); -LL + type T6 = async unsafe extern "C" fn(); +LL + type T6 = async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -107,7 +107,7 @@ LL | type T6 = const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type T6 = const async unsafe extern "C" fn(); -LL + type T6 = const unsafe extern "C" fn(); +LL + type T6 = const unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -121,7 +121,7 @@ LL | type FT0 = for<'a> const fn(); help: remove the `const` qualifier | LL - type FT0 = for<'a> const fn(); -LL + type FT0 = for<'a> fn(); +LL + type FT0 = for<'a> fn(); | error: an `fn` pointer type cannot be `const` @@ -135,7 +135,7 @@ LL | type FT1 = for<'a> const extern "C" fn(); help: remove the `const` qualifier | LL - type FT1 = for<'a> const extern "C" fn(); -LL + type FT1 = for<'a> extern "C" fn(); +LL + type FT1 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -149,7 +149,7 @@ LL | type FT2 = for<'a> const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type FT2 = for<'a> const unsafe extern "C" fn(); -LL + type FT2 = for<'a> unsafe extern "C" fn(); +LL + type FT2 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -163,7 +163,7 @@ LL | type FT3 = for<'a> async fn(); help: remove the `async` qualifier | LL - type FT3 = for<'a> async fn(); -LL + type FT3 = for<'a> fn(); +LL + type FT3 = for<'a> fn(); | error: an `fn` pointer type cannot be `async` @@ -177,7 +177,7 @@ LL | type FT4 = for<'a> async extern "C" fn(); help: remove the `async` qualifier | LL - type FT4 = for<'a> async extern "C" fn(); -LL + type FT4 = for<'a> extern "C" fn(); +LL + type FT4 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -191,7 +191,7 @@ LL | type FT5 = for<'a> async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type FT5 = for<'a> async unsafe extern "C" fn(); -LL + type FT5 = for<'a> unsafe extern "C" fn(); +LL + type FT5 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -205,7 +205,7 @@ LL | type FT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type FT6 = for<'a> const async unsafe extern "C" fn(); -LL + type FT6 = for<'a> async unsafe extern "C" fn(); +LL + type FT6 = for<'a> async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -219,7 +219,7 @@ LL | type FT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type FT6 = for<'a> const async unsafe extern "C" fn(); -LL + type FT6 = for<'a> const unsafe extern "C" fn(); +LL + type FT6 = for<'a> const unsafe extern "C" fn(); | error[E0308]: mismatched types From 314238f92ec062df1fae10cc66da7c2df2912282 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 27 Jan 2025 10:08:05 -0800 Subject: [PATCH 175/342] Flip the `rustc-rayon`/`indexmap` dependency order [`rustc-rayon v0.5.1`](https://github.com/rust-lang/rustc-rayon/pull/14) added `indexmap` implementations that will allow `indexmap` to drop its own "internal-only" implementations. (This is separate from `indexmap`'s implementation for normal `rayon`.) --- Cargo.lock | 6 +++--- compiler/rustc_data_structures/Cargo.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 979198cece80..04bb96be369d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1859,7 +1859,6 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", - "rustc-rayon", "serde", ] @@ -3240,11 +3239,12 @@ dependencies = [ [[package]] name = "rustc-rayon" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81aadc8837ca6ecebe0fe1353f15df83b3b3cc2cf7a8afd571bc22aa121710" +checksum = "2cd9fb077db982d7ceb42a90471e5a69a990b58f71e06f0d8340bb2cf35eb751" dependencies = [ "either", + "indexmap", "rustc-rayon-core", ] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 889a8299c18f..8e5af33d8b67 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -10,11 +10,11 @@ bitflags = "2.4.1" either = "1.0" elsa = "=1.7.1" ena = "0.14.3" -indexmap = { version = "2.4.0", features = ["rustc-rayon"] } +indexmap = "2.4.0" jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "11" rustc-hash = "2.0.0" -rustc-rayon = "0.5.0" +rustc-rayon = { version = "0.5.1", features = ["indexmap"] } rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } From fa4589bcebfd995ec421f2ddd2351bc374a8273c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 23 Jan 2025 00:00:00 +0000 Subject: [PATCH 176/342] Locate asan-odr-win with other sanitizer tests --- tests/ui/{asan-odr-win => sanitizer}/asan_odr_windows.rs | 0 tests/ui/{asan-odr-win => sanitizer}/auxiliary/asan_odr_win-2.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{asan-odr-win => sanitizer}/asan_odr_windows.rs (100%) rename tests/ui/{asan-odr-win => sanitizer}/auxiliary/asan_odr_win-2.rs (100%) diff --git a/tests/ui/asan-odr-win/asan_odr_windows.rs b/tests/ui/sanitizer/asan_odr_windows.rs similarity index 100% rename from tests/ui/asan-odr-win/asan_odr_windows.rs rename to tests/ui/sanitizer/asan_odr_windows.rs diff --git a/tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs b/tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs similarity index 100% rename from tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs rename to tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs From 1f4309cec4462ea76a2c7a89fa0aff3d1782e60b Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 23 Jan 2025 19:08:37 -0500 Subject: [PATCH 177/342] Fix 2/4 tests skipped by opt-dist --- .../dist-aarch64-linux/Dockerfile | 1 + .../host-x86_64/dist-x86_64-linux/Dockerfile | 2 ++ src/ci/github-actions/jobs.yml | 2 +- src/tools/opt-dist/src/main.rs | 20 +++++++------------ 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile index 6f33c6321818..2b8a3f829c60 100644 --- a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -20,6 +20,7 @@ RUN yum upgrade -y && \ gcc-c++ \ git \ glibc-devel \ + glibc-static \ libedit-devel \ libstdc++-devel \ make \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 3a3962305825..0b4682ac32ba 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -21,6 +21,8 @@ RUN yum upgrade -y && \ git \ glibc-devel.i686 \ glibc-devel.x86_64 \ + glibc-static.i686 \ + glibc-static.x86_64 \ libedit-devel \ libstdc++-devel.i686 \ libstdc++-devel.x86_64 \ diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 7730d29d28f6..4c4863e2b4b3 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -86,7 +86,7 @@ envs: # builds) # - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain # - # If you *want* these to happen however, temporarily uncomment it before triggering a try build. + # If you *want* these to happen however, temporarily comment it before triggering a try build. DIST_TRY_BUILD: 1 auto: diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index aa05b5f0e76c..04de3493ea29 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -148,18 +148,15 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> let is_aarch64 = target_triple.starts_with("aarch64"); - let mut skip_tests = vec![ - // Fails because of linker errors, as of June 2023. - "tests/ui/process/nofile-limit.rs".to_string(), - ]; - - if is_aarch64 { - skip_tests.extend([ + let skip_tests = if is_aarch64 { + vec![ // Those tests fail only inside of Docker on aarch64, as of December 2024 "tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs".to_string(), "tests/ui/consts/large_const_alloc.rs".to_string(), - ]); - } + ] + } else { + vec![] + }; let checkout_dir = Utf8PathBuf::from("/checkout"); let env = EnvironmentBuilder::default() @@ -191,10 +188,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .build_dir(checkout_dir) .shared_llvm(false) .use_bolt(false) - .skipped_tests(vec![ - // Fails as of June 2023. - "tests\\codegen\\vec-shrink-panik.rs".to_string(), - ]) + .skipped_tests(vec![]) .build()?; (env, shared.build_args) From 43b29da91e12de37e28a60d5d97bced637258d4b Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 14 Jan 2025 20:22:14 +0900 Subject: [PATCH 178/342] correct suggestion for `redundant_closure` in a `no_std` environment --- clippy_lints/src/eta_reduction.rs | 31 +++++++++++++++++-------------- tests/ui/eta_nostd.fixed | 10 ++++++++++ tests/ui/eta_nostd.rs | 10 ++++++++++ tests/ui/eta_nostd.stderr | 11 +++++++++++ 4 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 tests/ui/eta_nostd.fixed create mode 100644 tests/ui/eta_nostd.rs create mode 100644 tests/ui/eta_nostd.stderr diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 6cba6e2e9c70..f93dc1a87335 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,9 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id}; +use clippy_utils::{ + get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, +}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; @@ -101,19 +103,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }; if body.value.span.from_expansion() { - if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); - } + if body.params.is_empty() + && let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) + { + let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" }; + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + format!("{vec_crate}::vec::Vec::new"), + Applicability::MachineApplicable, + ); } // skip `foo(|| macro!())` return; diff --git a/tests/ui/eta_nostd.fixed b/tests/ui/eta_nostd.fixed new file mode 100644 index 000000000000..23059c52b67f --- /dev/null +++ b/tests/ui/eta_nostd.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::redundant_closure)] +#![no_std] + +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_13895() { + let _: Option> = true.then(alloc::vec::Vec::new); +} diff --git a/tests/ui/eta_nostd.rs b/tests/ui/eta_nostd.rs new file mode 100644 index 000000000000..ae44ac348c64 --- /dev/null +++ b/tests/ui/eta_nostd.rs @@ -0,0 +1,10 @@ +#![warn(clippy::redundant_closure)] +#![no_std] + +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_13895() { + let _: Option> = true.then(|| vec![]); +} diff --git a/tests/ui/eta_nostd.stderr b/tests/ui/eta_nostd.stderr new file mode 100644 index 000000000000..4dfef43efa47 --- /dev/null +++ b/tests/ui/eta_nostd.stderr @@ -0,0 +1,11 @@ +error: redundant closure + --> tests/ui/eta_nostd.rs:9:40 + | +LL | let _: Option> = true.then(|| vec![]); + | ^^^^^^^^^ help: replace the closure with `Vec::new`: `alloc::vec::Vec::new` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` + +error: aborting due to 1 previous error + From e146039e179aa995e7379a7d5cfb830c944239b4 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 14 Jan 2025 20:28:12 +0900 Subject: [PATCH 179/342] correct suggestion for `repeat_vec_with_capacity` in a `no_std` environment --- clippy_lints/src/repeat_vec_with_capacity.rs | 8 ++++++-- tests/ui/repeat_vec_with_capacity_nostd.fixed | 10 ++++++++++ tests/ui/repeat_vec_with_capacity_nostd.rs | 10 ++++++++++ tests/ui/repeat_vec_with_capacity_nostd.stderr | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/ui/repeat_vec_with_capacity_nostd.fixed create mode 100644 tests/ui/repeat_vec_with_capacity_nostd.rs create mode 100644 tests/ui/repeat_vec_with_capacity_nostd.stderr diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index f54cafffb83c..5dddf9263a35 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; -use clippy_utils::{expr_or_init, fn_def_id}; +use clippy_utils::{expr_or_init, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -93,6 +93,7 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { && let ExprKind::Call(_, [repeat_expr]) = expr.kind && fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did)) && !repeat_expr.span.from_expansion() + && let Some(exec_context) = std_or_core(cx) { emit_lint( cx, @@ -100,7 +101,10 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { "iter::repeat", "none of the yielded `Vec`s will have the requested capacity", "if you intended to create an iterator that yields `Vec`s with an initial capacity, try", - format!("std::iter::repeat_with(|| {})", snippet(cx, repeat_expr.span, "..")), + format!( + "{exec_context}::iter::repeat_with(|| {})", + snippet(cx, repeat_expr.span, "..") + ), ); } } diff --git a/tests/ui/repeat_vec_with_capacity_nostd.fixed b/tests/ui/repeat_vec_with_capacity_nostd.fixed new file mode 100644 index 000000000000..ef316f1def41 --- /dev/null +++ b/tests/ui/repeat_vec_with_capacity_nostd.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::repeat_vec_with_capacity)] +#![allow(clippy::manual_repeat_n)] +#![no_std] +use core::iter; +extern crate alloc; +use alloc::vec::Vec; + +fn nostd() { + let _: Vec> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); +} diff --git a/tests/ui/repeat_vec_with_capacity_nostd.rs b/tests/ui/repeat_vec_with_capacity_nostd.rs new file mode 100644 index 000000000000..83b418a56674 --- /dev/null +++ b/tests/ui/repeat_vec_with_capacity_nostd.rs @@ -0,0 +1,10 @@ +#![warn(clippy::repeat_vec_with_capacity)] +#![allow(clippy::manual_repeat_n)] +#![no_std] +use core::iter; +extern crate alloc; +use alloc::vec::Vec; + +fn nostd() { + let _: Vec> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); +} diff --git a/tests/ui/repeat_vec_with_capacity_nostd.stderr b/tests/ui/repeat_vec_with_capacity_nostd.stderr new file mode 100644 index 000000000000..39364d09b961 --- /dev/null +++ b/tests/ui/repeat_vec_with_capacity_nostd.stderr @@ -0,0 +1,16 @@ +error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity + --> tests/ui/repeat_vec_with_capacity_nostd.rs:9:27 + | +LL | let _: Vec> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: none of the yielded `Vec`s will have the requested capacity + = note: `-D clippy::repeat-vec-with-capacity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]` +help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try + | +LL | let _: Vec> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + From cb0a479d1f15831f99f0c8b6b730b3c804c5f9e1 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 14 Jan 2025 20:33:23 +0900 Subject: [PATCH 180/342] don't suggest to use `std::vec::Vec` for `single_range_in_vec_init` in a `no_std` environment --- clippy_lints/src/single_range_in_vec_init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index 9737b84cdb9c..2d989b1cf0ba 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_trait_def_id; use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, is_no_std_crate}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -125,7 +125,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { span, format!("{suggested_type} of `Range` that is only one element"), |diag| { - if should_emit_every_value { + if should_emit_every_value && !is_no_std_crate(cx) { diag.span_suggestion( span, "if you wanted a `Vec` that contains the entire range, try", From d99eae432563b955da5bddff8ca678ac3f7eb9c8 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 14 Jan 2025 20:37:35 +0900 Subject: [PATCH 181/342] correct suggestion for `drain_collect` in a `no_std` environment --- clippy_lints/src/methods/drain_collect.rs | 7 ++++--- tests/ui/drain_collect_nostd.fixed | 8 ++++++++ tests/ui/drain_collect_nostd.rs | 8 ++++++++ tests/ui/drain_collect_nostd.stderr | 11 +++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/ui/drain_collect_nostd.fixed create mode 100644 tests/ui/drain_collect_nostd.rs create mode 100644 tests/ui/drain_collect_nostd.stderr diff --git a/clippy_lints/src/methods/drain_collect.rs b/clippy_lints/src/methods/drain_collect.rs index 10360b4817bc..cbf713a3b17c 100644 --- a/clippy_lints/src/methods/drain_collect.rs +++ b/clippy_lints/src/methods/drain_collect.rs @@ -1,8 +1,8 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_range_full; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; @@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re .then_some("Vec") .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) + && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); let sugg = if let ty::Ref(..) = recv_ty.kind() { - format!("std::mem::take({recv})") + format!("{exec_context}::mem::take({recv})") } else { - format!("std::mem::take(&mut {recv})") + format!("{exec_context}::mem::take(&mut {recv})") }; span_lint_and_sugg( diff --git a/tests/ui/drain_collect_nostd.fixed b/tests/ui/drain_collect_nostd.fixed new file mode 100644 index 000000000000..a4ab2956f2a6 --- /dev/null +++ b/tests/ui/drain_collect_nostd.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::drain_collect)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn remove_all(v: &mut Vec) -> Vec { + core::mem::take(v) +} diff --git a/tests/ui/drain_collect_nostd.rs b/tests/ui/drain_collect_nostd.rs new file mode 100644 index 000000000000..a8be1ce6bbd3 --- /dev/null +++ b/tests/ui/drain_collect_nostd.rs @@ -0,0 +1,8 @@ +#![warn(clippy::drain_collect)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn remove_all(v: &mut Vec) -> Vec { + v.drain(..).collect() +} diff --git a/tests/ui/drain_collect_nostd.stderr b/tests/ui/drain_collect_nostd.stderr new file mode 100644 index 000000000000..91b38932fee7 --- /dev/null +++ b/tests/ui/drain_collect_nostd.stderr @@ -0,0 +1,11 @@ +error: you seem to be trying to move all elements into a new `Vec` + --> tests/ui/drain_collect_nostd.rs:7:5 + | +LL | v.drain(..).collect() + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | + = note: `-D clippy::drain-collect` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` + +error: aborting due to 1 previous error + From 3b0b8b080eedfeadc366c051d1c14307c7a7455e Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Tue, 14 Jan 2025 20:45:11 +0900 Subject: [PATCH 182/342] correct suggestion for `map_with_unused_argument_over_ranges` in a `no_std` environment --- .../map_with_unused_argument_over_ranges.rs | 5 +++-- ...p_with_unused_argument_over_ranges_nostd.fixed | 8 ++++++++ .../map_with_unused_argument_over_ranges_nostd.rs | 8 ++++++++ ..._with_unused_argument_over_ranges_nostd.stderr | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/ui/map_with_unused_argument_over_ranges_nostd.fixed create mode 100644 tests/ui/map_with_unused_argument_over_ranges_nostd.rs create mode 100644 tests/ui/map_with_unused_argument_over_ranges_nostd.stderr diff --git a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 1ebb71e251ab..78656ace831d 100644 --- a/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{eager_or_lazy, higher, usage}; +use clippy_utils::{eager_or_lazy, higher, std_or_core, usage}; use rustc_ast::LitKind; use rustc_ast::ast::RangeLimits; use rustc_data_structures::packed::Pu128; @@ -75,6 +75,7 @@ pub(super) fn check( } = body_hir && !usage::BindingUsageFinder::are_params_used(cx, body_hir) && let Some(count) = extract_count_with_applicability(cx, range, &mut applicability) + && let Some(exec_context) = std_or_core(cx) { let method_to_use_name; let new_span; @@ -105,7 +106,7 @@ pub(super) fn check( let mut parts = vec![ ( receiver.span.to(method_call_span), - format!("std::iter::{method_to_use_name}"), + format!("{exec_context}::iter::{method_to_use_name}"), ), new_span, ]; diff --git a/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed b/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed new file mode 100644 index 000000000000..65e59774905c --- /dev/null +++ b/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::map_with_unused_argument_over_ranges)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn nostd(v: &mut [i32]) { + let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect(); +} diff --git a/tests/ui/map_with_unused_argument_over_ranges_nostd.rs b/tests/ui/map_with_unused_argument_over_ranges_nostd.rs new file mode 100644 index 000000000000..dda7a69b33f9 --- /dev/null +++ b/tests/ui/map_with_unused_argument_over_ranges_nostd.rs @@ -0,0 +1,8 @@ +#![warn(clippy::map_with_unused_argument_over_ranges)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn nostd(v: &mut [i32]) { + let _: Vec<_> = (0..10).map(|_| 3 + 1).collect(); +} diff --git a/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr b/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr new file mode 100644 index 000000000000..d47f3d09175b --- /dev/null +++ b/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr @@ -0,0 +1,15 @@ +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges_nostd.rs:7:21 + | +LL | let _: Vec<_> = (0..10).map(|_| 3 + 1).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]` +help: remove the explicit range and use `repeat_n` + | +LL | let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect(); + | ~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~ + +error: aborting due to 1 previous error + From 1cbb062e54e376abe8b6f146f64c0d849e30c476 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 Jan 2025 19:37:07 +0000 Subject: [PATCH 183/342] Type level consts can show up in MIR type checker --- compiler/rustc_borrowck/src/type_check/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e0196d55f20a..a2ef5588f48f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -349,8 +349,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_) => { - bug!("should not encounter unevaluated Const::Ty here, got {:?}", ct) + ty::ConstKind::Unevaluated(uv) => { + Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) } _ => None, }, From 057313b7a607513d6c1aa82e454e2370025f3b9a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jan 2025 22:13:00 +0000 Subject: [PATCH 184/342] Reapply "Auto merge of #133734 - scottmcm:lower-indexing-to-ptrmetadata, r=davidtwco,RalfJung" This reverts commit 122a55bb442bd1995df9cf9b36e6f65ed3ef4a1d. --- .../src/check_consts/check.rs | 25 +- .../rustc_const_eval/src/interpret/step.rs | 13 +- .../src/builder/expr/as_place.rs | 92 +++++- .../rustc_mir_transform/src/instsimplify.rs | 13 - compiler/rustc_mir_transform/src/validate.rs | 8 - compiler/rustc_span/src/hygiene.rs | 4 + ...fg-pre-optimizations.after.panic-abort.mir | 8 +- ...g-pre-optimizations.after.panic-unwind.mir | 8 +- ...rray_and_slice.index_array.built.after.mir | 31 ++ ....index_const_generic_array.built.after.mir | 31 ++ ...ray_and_slice.index_custom.built.after.mir | 34 ++ ..._and_slice.index_mut_slice.built.after.mir | 34 ++ ...rray_and_slice.index_slice.built.after.mir | 32 ++ .../mir-opt/building/index_array_and_slice.rs | 71 ++++ ...rray_index.main.GVN.32bit.panic-abort.diff | 11 +- ...ray_index.main.GVN.32bit.panic-unwind.diff | 11 +- ...rray_index.main.GVN.64bit.panic-abort.diff | 11 +- ...ray_index.main.GVN.64bit.panic-unwind.diff | 11 +- ...for_slices.main.GVN.32bit.panic-abort.diff | 7 +- ...or_slices.main.GVN.32bit.panic-unwind.diff | 7 +- ...for_slices.main.GVN.64bit.panic-abort.diff | 7 +- ...or_slices.main.GVN.64bit.panic-unwind.diff | 7 +- ...rray_index.main.GVN.32bit.panic-abort.diff | 11 +- ...ray_index.main.GVN.32bit.panic-unwind.diff | 11 +- ...rray_index.main.GVN.64bit.panic-abort.diff | 11 +- ...ray_index.main.GVN.64bit.panic-unwind.diff | 11 +- .../repeat.main.GVN.32bit.panic-abort.diff | 11 +- .../repeat.main.GVN.32bit.panic-unwind.diff | 11 +- .../repeat.main.GVN.64bit.panic-abort.diff | 11 +- .../repeat.main.GVN.64bit.panic-unwind.diff | 11 +- .../slice_len.main.GVN.32bit.panic-abort.diff | 7 +- ...slice_len.main.GVN.32bit.panic-unwind.diff | 7 +- .../slice_len.main.GVN.64bit.panic-abort.diff | 7 +- ...slice_len.main.GVN.64bit.panic-unwind.diff | 7 +- ...ssue_107511.main.CopyProp.panic-abort.diff | 12 +- ...sue_107511.main.CopyProp.panic-unwind.diff | 12 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 11 +- ....DataflowConstProp.32bit.panic-unwind.diff | 11 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 11 +- ....DataflowConstProp.64bit.panic-unwind.diff | 11 +- .../dataflow-const-prop/array_index.rs | 3 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 11 +- ....DataflowConstProp.32bit.panic-unwind.diff | 11 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 11 +- ....DataflowConstProp.64bit.panic-unwind.diff | 11 +- .../dataflow-const-prop/large_array_index.rs | 2 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 11 +- ....DataflowConstProp.32bit.panic-unwind.diff | 11 +- ...n.DataflowConstProp.64bit.panic-abort.diff | 11 +- ....DataflowConstProp.64bit.panic-unwind.diff | 11 +- tests/mir-opt/dataflow-const-prop/repeat.rs | 3 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 77 ----- ....DataflowConstProp.32bit.panic-unwind.diff | 77 ----- ...n.DataflowConstProp.64bit.panic-abort.diff | 77 ----- ....DataflowConstProp.64bit.panic-unwind.diff | 77 ----- .../mir-opt/dataflow-const-prop/slice_len.rs | 34 -- ...nstant_index_overflow.GVN.panic-abort.diff | 4 +- ...stant_index_overflow.GVN.panic-unwind.diff | 4 +- ...bounds_checks_lengths.GVN.panic-abort.diff | 72 +++++ ...ounds_checks_lengths.GVN.panic-unwind.diff | 72 +++++ .../gvn.repeated_index.GVN.panic-abort.diff | 48 ++- .../gvn.repeated_index.GVN.panic-unwind.diff | 48 ++- tests/mir-opt/gvn.rs | 20 ++ ...e_ptr_same_provenance.GVN.panic-abort.diff | 302 +++++++++--------- ..._ptr_same_provenance.GVN.panic-unwind.diff | 302 +++++++++--------- ...implify-after-simplifycfg.panic-abort.diff | 77 ----- ...mplify-after-simplifycfg.panic-unwind.diff | 77 ----- .../mir-opt/instsimplify/combine_array_len.rs | 15 - tests/mir-opt/issue_72181.foo.built.after.mir | 9 +- .../mir-opt/issue_72181.main.built.after.mir | 9 +- tests/mir-opt/issue_91633.foo.built.after.mir | 12 +- tests/mir-opt/issue_91633.fun.built.after.mir | 2 +- ...array_len.array_bound.GVN.panic-abort.diff | 20 +- ...rray_len.array_bound.GVN.panic-unwind.diff | 20 +- ...y_len.array_bound_mut.GVN.panic-abort.diff | 43 +-- ..._len.array_bound_mut.GVN.panic-unwind.diff | 43 +-- ....bound.LowerSliceLenCalls.panic-abort.diff | 2 +- ...bound.LowerSliceLenCalls.panic-unwind.diff | 2 +- ...egion_subtyping_basic.main.nll.0.32bit.mir | 47 ++- ...egion_subtyping_basic.main.nll.0.64bit.mir | 47 ++- ...o_variable.main.GVN.32bit.panic-abort.diff | 26 +- ..._variable.main.GVN.32bit.panic-unwind.diff | 26 +- ...o_variable.main.GVN.64bit.panic-abort.diff | 26 +- ..._variable.main.GVN.64bit.panic-unwind.diff | 26 +- ...acementOfAggregates.32bit.panic-abort.diff | 38 ++- ...cementOfAggregates.32bit.panic-unwind.diff | 38 ++- ...acementOfAggregates.64bit.panic-abort.diff | 38 ++- ...cementOfAggregates.64bit.panic-unwind.diff | 38 ++- tests/mir-opt/pre-codegen/slice_index.rs | 2 +- ...dex_usize.PreCodegen.after.panic-abort.mir | 2 +- ...ex_usize.PreCodegen.after.panic-unwind.mir | 2 +- ...ange_loop.PreCodegen.after.panic-abort.mir | 30 +- ...nge_loop.PreCodegen.after.panic-unwind.mir | 30 +- tests/ui/borrowck/borrowck-describe-lvalue.rs | 1 - .../borrowck/borrowck-describe-lvalue.stderr | 25 +- .../diagnostics/arrays.rs | 6 +- .../diagnostics/arrays.stderr | 40 +-- tests/ui/consts/issue-65348.rs | 4 +- tests/ui/stable-mir-print/operands.stdout | 294 +++++++++-------- 99 files changed, 1432 insertions(+), 1655 deletions(-) create mode 100644 tests/mir-opt/building/index_array_and_slice.index_array.built.after.mir create mode 100644 tests/mir-opt/building/index_array_and_slice.index_const_generic_array.built.after.mir create mode 100644 tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir create mode 100644 tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir create mode 100644 tests/mir-opt/building/index_array_and_slice.index_slice.built.after.mir create mode 100644 tests/mir-opt/building/index_array_and_slice.rs delete mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff delete mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff delete mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff delete mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff delete mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.rs create mode 100644 tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-abort.diff create mode 100644 tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-unwind.diff delete mode 100644 tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff delete mode 100644 tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff delete mode 100644 tests/mir-opt/instsimplify/combine_array_len.rs diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ed34996a7a7d..c3b609b642df 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -586,12 +586,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ) => {} Rvalue::ShallowInitBox(_, _) => {} - Rvalue::UnaryOp(_, operand) => { + Rvalue::UnaryOp(op, operand) => { let ty = operand.ty(self.body, self.tcx); - if is_int_bool_float_or_char(ty) { - // Int, bool, float, and char operations are fine. - } else { - span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty); + match op { + UnOp::Not | UnOp::Neg => { + if is_int_bool_float_or_char(ty) { + // Int, bool, float, and char operations are fine. + } else { + span_bug!( + self.span, + "non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}", + ); + } + } + UnOp::PtrMetadata => { + if !ty.is_ref() && !ty.is_unsafe_ptr() { + span_bug!( + self.span, + "non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}", + ); + } + } } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index b61865be6678..a26c2eca107c 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::source_map::Spanned; +use rustc_span::{DesugaringKind, Span}; use rustc_target::callconv::FnAbi; use tracing::{info, instrument, trace}; @@ -80,7 +81,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { use rustc_middle::mir::StatementKind::*; match &stmt.kind { - Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?, + Assign(box (place, rvalue)) => { + self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)? + } SetDiscriminant { place, variant_index } => { let dest = self.eval_place(**place)?; @@ -159,6 +162,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, rvalue: &mir::Rvalue<'tcx>, place: mir::Place<'tcx>, + span: Span, ) -> InterpResult<'tcx> { let dest = self.eval_place(place)?; // FIXME: ensure some kind of non-aliasing between LHS and RHS? @@ -250,8 +254,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout); - if !place_base_raw { + if !place_base_raw + && span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow) + { // If this was not already raw, it needs retagging. + // As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)` + // from indexing. (Really we should not do any retag on `&raw` but that does not + // currently work with Stacked Borrows.) val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?; } self.write_immediate(*val, &dest)?; diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index b1851e79d5c6..7634bc74fcbb 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance}; use rustc_middle::{bug, span_bug}; -use rustc_span::Span; +use rustc_span::{DesugaringKind, Span}; use tracing::{debug, instrument, trace}; use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard}; @@ -630,6 +630,80 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(base_place.index(idx)) } + /// Given a place that's either an array or a slice, returns an operand + /// with the length of the array/slice. + /// + /// For arrays it'll be `Operand::Constant` with the actual length; + /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. + fn len_of_slice_or_array( + &mut self, + block: BasicBlock, + place: Place<'tcx>, + span: Span, + source_info: SourceInfo, + ) -> Operand<'tcx> { + let place_ty = place.ty(&self.local_decls, self.tcx).ty; + let usize_ty = self.tcx.types.usize; + + match place_ty.kind() { + ty::Array(_elem_ty, len_const) => { + // We know how long an array is, so just use that as a constant + // directly -- no locals needed. We do need one statement so + // that borrow- and initialization-checking consider it used, + // though. FIXME: Do we really *need* to count this as a use? + // Could partial array tracking work off something else instead? + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); + let const_ = Const::Ty(self.tcx.types.usize, *len_const); + Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ })) + } + ty::Slice(_elem_ty) => { + let ptr_or_ref = if let [PlaceElem::Deref] = place.projection[..] + && let local_ty = self.local_decls[place.local].ty + && local_ty.is_trivially_pure_clone_copy() + { + // It's extremely common that we have something that can be + // directly passed to `PtrMetadata`, so avoid an unnecessary + // temporary and statement in those cases. Note that we can + // only do that for `Copy` types -- not `&mut [_]` -- because + // the MIR we're building here needs to pass NLL later. + Operand::Copy(Place::from(place.local)) + } else { + let len_span = self.tcx.with_stable_hashing_context(|hcx| { + let span = source_info.span; + span.mark_with_reason( + None, + DesugaringKind::IndexBoundsCheckReborrow, + span.edition(), + hcx, + ) + }); + let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); + let slice_ptr = self.temp(ptr_ty, span); + self.cfg.push_assign( + block, + SourceInfo { span: len_span, ..source_info }, + slice_ptr, + Rvalue::RawPtr(Mutability::Not, place), + ); + Operand::Move(slice_ptr) + }; + + let len = self.temp(usize_ty, span); + self.cfg.push_assign( + block, + source_info, + len, + Rvalue::UnaryOp(UnOp::PtrMetadata, ptr_or_ref), + ); + + Operand::Move(len) + } + _ => { + span_bug!(span, "len called on place of type {place_ty:?}") + } + } + } + fn bounds_check( &mut self, block: BasicBlock, @@ -638,25 +712,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_span: Span, source_info: SourceInfo, ) -> BasicBlock { - let usize_ty = self.tcx.types.usize; - let bool_ty = self.tcx.types.bool; - // bounds check: - let len = self.temp(usize_ty, expr_span); - let lt = self.temp(bool_ty, expr_span); + let slice = slice.to_place(self); // len = len(slice) - self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self))); + let len = self.len_of_slice_or_array(block, slice, expr_span, source_info); + // lt = idx < len + let bool_ty = self.tcx.types.bool; + let lt = self.temp(bool_ty, expr_span); self.cfg.push_assign( block, source_info, lt, Rvalue::BinaryOp( BinOp::Lt, - Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))), + Box::new((Operand::Copy(Place::from(index)), len.to_copy())), ), ); - let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; + let msg = BoundsCheck { len, index: Operand::Copy(Place::from(index)) }; + // assert!(lt, "...") self.assert(block, Operand::Move(lt), true, msg, expr_span) } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 4b9ebd40b857..3dc4edaaa5ae 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -46,7 +46,6 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { } ctx.simplify_bool_cmp(rvalue); ctx.simplify_ref_deref(rvalue); - ctx.simplify_len(rvalue); ctx.simplify_ptr_aggregate(rvalue); ctx.simplify_cast(rvalue); ctx.simplify_repeated_aggregate(rvalue); @@ -166,18 +165,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } } - /// Transform `Len([_; N])` ==> `N`. - fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::Len(ref place) = *rvalue { - let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = *place_ty.kind() { - let const_ = Const::Ty(self.tcx.types.usize, len); - let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; - *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); - } - } - } - /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 026923ad786b..5881264cba52 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1128,14 +1128,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } UnOp::PtrMetadata => { - if !matches!(self.body.phase, MirPhase::Runtime(_)) { - // It would probably be fine to support this in earlier phases, but at - // the time of writing it's only ever introduced from intrinsic - // lowering or other runtime-phase optimization passes, so earlier - // things can just `bug!` on it. - self.fail(location, "PtrMetadata should be in runtime MIR only"); - } - check_kinds!( a, "Cannot PtrMetadata non-pointer non-reference type {:?}", diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a5826137181d..3cdae437b7d4 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1163,6 +1163,9 @@ pub enum DesugaringKind { WhileLoop, /// `async Fn()` bound modifier BoundModifier, + /// Marks a `&raw const *_1` needed as part of getting the length of a mutable + /// slice for the bounds check, so that MIRI's retag handling can recognize it. + IndexBoundsCheckReborrow, } impl DesugaringKind { @@ -1179,6 +1182,7 @@ impl DesugaringKind { DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", + DesugaringKind::IndexBoundsCheckReborrow => "slice indexing", } } } diff --git a/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-abort.mir b/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-abort.mir index a467987e8861..8d9176ef301f 100644 --- a/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-abort.mir +++ b/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-abort.mir @@ -7,8 +7,7 @@ fn main() -> () { let mut _5: u32; let mut _6: *mut usize; let _7: usize; - let mut _8: usize; - let mut _9: bool; + let mut _8: bool; scope 1 { debug x => _1; let mut _2: usize; @@ -41,9 +40,8 @@ fn main() -> () { StorageDead(_6); StorageLive(_7); _7 = copy _2; - _8 = Len(_1); - _9 = Lt(copy _7, copy _8); - assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind unreachable]; + _8 = Lt(copy _7, const 3_usize); + assert(move _8, "index out of bounds: the length is {} but the index is {}", const 3_usize, copy _7) -> [success: bb2, unwind unreachable]; } bb2: { diff --git a/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-unwind.mir b/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-unwind.mir index bd7365543bdc..e1df0e3e2a3b 100644 --- a/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-unwind.mir +++ b/tests/mir-opt/array_index_is_temporary.main.SimplifyCfg-pre-optimizations.after.panic-unwind.mir @@ -7,8 +7,7 @@ fn main() -> () { let mut _5: u32; let mut _6: *mut usize; let _7: usize; - let mut _8: usize; - let mut _9: bool; + let mut _8: bool; scope 1 { debug x => _1; let mut _2: usize; @@ -41,9 +40,8 @@ fn main() -> () { StorageDead(_6); StorageLive(_7); _7 = copy _2; - _8 = Len(_1); - _9 = Lt(copy _7, copy _8); - assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind continue]; + _8 = Lt(copy _7, const 3_usize); + assert(move _8, "index out of bounds: the length is {} but the index is {}", const 3_usize, copy _7) -> [success: bb2, unwind continue]; } bb2: { diff --git a/tests/mir-opt/building/index_array_and_slice.index_array.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_array.built.after.mir new file mode 100644 index 000000000000..d28a2031013f --- /dev/null +++ b/tests/mir-opt/building/index_array_and_slice.index_array.built.after.mir @@ -0,0 +1,31 @@ +// MIR for `index_array` after built + +fn index_array(_1: &[i32; 7], _2: usize) -> &i32 { + debug array => _1; + debug index => _2; + let mut _0: &i32; + let _3: &i32; + let _4: usize; + let mut _5: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _2; + FakeRead(ForIndex, (*_1)); + _5 = Lt(copy _4, const 7_usize); + assert(move _5, "index out of bounds: the length is {} but the index is {}", const 7_usize, copy _4) -> [success: bb1, unwind: bb2]; + } + + bb1: { + _3 = &(*_1)[_4]; + _0 = &(*_3); + StorageDead(_4); + StorageDead(_3); + return; + } + + bb2 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/index_array_and_slice.index_const_generic_array.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_const_generic_array.built.after.mir new file mode 100644 index 000000000000..e9627532c382 --- /dev/null +++ b/tests/mir-opt/building/index_array_and_slice.index_const_generic_array.built.after.mir @@ -0,0 +1,31 @@ +// MIR for `index_const_generic_array` after built + +fn index_const_generic_array(_1: &[i32; N], _2: usize) -> &i32 { + debug array => _1; + debug index => _2; + let mut _0: &i32; + let _3: &i32; + let _4: usize; + let mut _5: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _2; + FakeRead(ForIndex, (*_1)); + _5 = Lt(copy _4, const N); + assert(move _5, "index out of bounds: the length is {} but the index is {}", const N, copy _4) -> [success: bb1, unwind: bb2]; + } + + bb1: { + _3 = &(*_1)[_4]; + _0 = &(*_3); + StorageDead(_4); + StorageDead(_3); + return; + } + + bb2 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir new file mode 100644 index 000000000000..00f2b7e07d55 --- /dev/null +++ b/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir @@ -0,0 +1,34 @@ +// MIR for `index_custom` after built + +fn index_custom(_1: &WithSliceTail, _2: usize) -> &i32 { + debug custom => _1; + debug index => _2; + let mut _0: &i32; + let _3: &i32; + let _4: usize; + let mut _5: *const [i32]; + let mut _6: usize; + let mut _7: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _2; + _5 = &raw const ((*_1).1: [i32]); + _6 = PtrMetadata(move _5); + _7 = Lt(copy _4, copy _6); + assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2]; + } + + bb1: { + _3 = &((*_1).1: [i32])[_4]; + _0 = &(*_3); + StorageDead(_4); + StorageDead(_3); + return; + } + + bb2 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir new file mode 100644 index 000000000000..cb0b2f600c81 --- /dev/null +++ b/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir @@ -0,0 +1,34 @@ +// MIR for `index_mut_slice` after built + +fn index_mut_slice(_1: &mut [i32], _2: usize) -> &i32 { + debug slice => _1; + debug index => _2; + let mut _0: &i32; + let _3: &i32; + let _4: usize; + let mut _5: *const [i32]; + let mut _6: usize; + let mut _7: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _2; + _5 = &raw const (*_1); + _6 = PtrMetadata(move _5); + _7 = Lt(copy _4, copy _6); + assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2]; + } + + bb1: { + _3 = &(*_1)[_4]; + _0 = &(*_3); + StorageDead(_4); + StorageDead(_3); + return; + } + + bb2 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/index_array_and_slice.index_slice.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_slice.built.after.mir new file mode 100644 index 000000000000..0911df590497 --- /dev/null +++ b/tests/mir-opt/building/index_array_and_slice.index_slice.built.after.mir @@ -0,0 +1,32 @@ +// MIR for `index_slice` after built + +fn index_slice(_1: &[i32], _2: usize) -> &i32 { + debug slice => _1; + debug index => _2; + let mut _0: &i32; + let _3: &i32; + let _4: usize; + let mut _5: usize; + let mut _6: bool; + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = copy _2; + _5 = PtrMetadata(copy _1); + _6 = Lt(copy _4, copy _5); + assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind: bb2]; + } + + bb1: { + _3 = &(*_1)[_4]; + _0 = &(*_3); + StorageDead(_4); + StorageDead(_3); + return; + } + + bb2 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/index_array_and_slice.rs b/tests/mir-opt/building/index_array_and_slice.rs new file mode 100644 index 000000000000..16d0b983132d --- /dev/null +++ b/tests/mir-opt/building/index_array_and_slice.rs @@ -0,0 +1,71 @@ +//@ compile-flags: -C opt-level=0 + +// EMIT_MIR index_array_and_slice.index_array.built.after.mir +fn index_array(array: &[i32; 7], index: usize) -> &i32 { + // CHECK: bb0: + // CHECK: [[LT:_.+]] = Lt(copy _2, const 7_usize); + // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", const 7_usize, copy _2) -> [success: bb1, unwind + + // CHECK: bb1: + // CHECK: _0 = &(*_1)[_2]; + &array[index] +} + +// EMIT_MIR index_array_and_slice.index_const_generic_array.built.after.mir +fn index_const_generic_array(array: &[i32; N], index: usize) -> &i32 { + // CHECK: bb0: + // CHECK: [[LT:_.+]] = Lt(copy _2, const N); + // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", const N, copy _2) -> [success: bb1, unwind + + // CHECK: bb1: + // CHECK: _0 = &(*_1)[_2]; + &array[index] +} + +// EMIT_MIR index_array_and_slice.index_slice.built.after.mir +fn index_slice(slice: &[i32], index: usize) -> &i32 { + // CHECK: bb0: + // CHECK: [[LEN:_.+]] = PtrMetadata(copy _1); + // CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]); + // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1, + + // CHECK: bb1: + // CHECK: _0 = &(*_1)[_2]; + &slice[index] +} + +// EMIT_MIR index_array_and_slice.index_mut_slice.built.after.mir +fn index_mut_slice(slice: &mut [i32], index: usize) -> &i32 { + // While the filecheck here is identical to the above test, the emitted MIR is different. + // This cannot `copy _1` in the *built* MIR, only in the *runtime* MIR. + + // CHECK: bb0: + // CHECK: [[LEN:_.+]] = PtrMetadata(copy _1); + // CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]); + // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1, + + // CHECK: bb1: + // CHECK: _0 = &(*_1)[_2]; + &slice[index] +} + +struct WithSliceTail(f64, [i32]); + +// EMIT_MIR index_array_and_slice.index_custom.built.after.mir +fn index_custom(custom: &WithSliceTail, index: usize) -> &i32 { + // CHECK: bb0: + // CHECK: [[PTR:_.+]] = &raw const ((*_1).1: [i32]); + // CHECK: [[LEN:_.+]] = PtrMetadata(move [[PTR]]); + // CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]); + // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1, + + // CHECK: bb1: + // CHECK: _0 = &((*_1).1: [i32])[_2]; + &custom.1[index] +} + +fn main() { + index_array(&[1, 2, 3, 4, 5, 6, 7], 3); + index_slice(&[1, 2, 3, 4, 5, 6, 7][..], 3); + _ = index_custom; +} diff --git a/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-abort.diff index e754af95ce39..3a5a8d009912 100644 --- a/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-unwind.diff index e15a35c7fe94..62d6e6007e5c 100644 --- a/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/array_index.main.GVN.32bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-abort.diff index e754af95ce39..3a5a8d009912 100644 --- a/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-unwind.diff index e15a35c7fe94..62d6e6007e5c 100644 --- a/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/array_index.main.GVN.64bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-abort.diff index 15d301403671..b7cb37a335fa 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-abort.diff @@ -32,11 +32,12 @@ StorageLive(_5); StorageLive(_6); _6 = const 3_usize; - _7 = Len((*_1)); +- _7 = PtrMetadata(copy _1); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; -+ _8 = Lt(const 3_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const false; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-unwind.diff index dd411d84f9fb..50388cbac361 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.32bit.panic-unwind.diff @@ -32,11 +32,12 @@ StorageLive(_5); StorageLive(_6); _6 = const 3_usize; - _7 = Len((*_1)); +- _7 = PtrMetadata(copy _1); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; -+ _8 = Lt(const 3_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const false; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-abort.diff index 15d301403671..b7cb37a335fa 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-abort.diff @@ -32,11 +32,12 @@ StorageLive(_5); StorageLive(_6); _6 = const 3_usize; - _7 = Len((*_1)); +- _7 = PtrMetadata(copy _1); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; -+ _8 = Lt(const 3_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const false; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-unwind.diff index dd411d84f9fb..50388cbac361 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.GVN.64bit.panic-unwind.diff @@ -32,11 +32,12 @@ StorageLive(_5); StorageLive(_6); _6 = const 3_usize; - _7 = Len((*_1)); +- _7 = PtrMetadata(copy _1); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; -+ _8 = Lt(const 3_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const false; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-abort.diff index 49ea51deed69..3569998b13fa 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-unwind.diff index 103bfbcaf642..50b31c9ac136 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.GVN.32bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-abort.diff index 49ea51deed69..3569998b13fa 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-unwind.diff index 103bfbcaf642..50b31c9ac136 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.GVN.64bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-abort.diff index f7c1c2da01fc..a41668b6fa36 100644 --- a/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-abort.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind unreachable]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind unreachable]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-unwind.diff index 436773c85563..2313084b49e6 100644 --- a/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/repeat.main.GVN.32bit.panic-unwind.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind continue]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind continue]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-abort.diff index f7c1c2da01fc..a41668b6fa36 100644 --- a/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-abort.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind unreachable]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind unreachable]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-unwind.diff index 436773c85563..2313084b49e6 100644 --- a/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/repeat.main.GVN.64bit.panic-unwind.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind continue]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind continue]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff index 8a8ea5b7e200..0798b3039295 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff @@ -30,11 +30,12 @@ StorageDead(_3); StorageLive(_6); _6 = const 1_usize; - _7 = Len((*_2)); +- _7 = PtrMetadata(copy _2); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; -+ _8 = Lt(const 1_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff index f0c844884f67..c0b3d4d32190 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff @@ -30,11 +30,12 @@ StorageDead(_3); StorageLive(_6); _6 = const 1_usize; - _7 = Len((*_2)); +- _7 = PtrMetadata(copy _2); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; -+ _8 = Lt(const 1_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff index 8a8ea5b7e200..0798b3039295 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff @@ -30,11 +30,12 @@ StorageDead(_3); StorageLive(_6); _6 = const 1_usize; - _7 = Len((*_2)); +- _7 = PtrMetadata(copy _2); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; -+ _8 = Lt(const 1_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff index f0c844884f67..c0b3d4d32190 100644 --- a/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff @@ -30,11 +30,12 @@ StorageDead(_3); StorageLive(_6); _6 = const 1_usize; - _7 = Len((*_2)); +- _7 = PtrMetadata(copy _2); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; -+ _8 = Lt(const 1_usize, copy _7); -+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-abort.diff b/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-abort.diff index 6d967257df1f..689083dfc1d3 100644 --- a/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-abort.diff +++ b/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-abort.diff @@ -18,8 +18,7 @@ let mut _15: !; let mut _17: i32; let _18: usize; - let mut _19: usize; - let mut _20: bool; + let mut _19: bool; scope 1 { debug sum => _1; let _2: [i32; 4]; @@ -92,11 +91,10 @@ StorageLive(_17); - StorageLive(_18); - _18 = copy _16; - _19 = Len(_2); -- _20 = Lt(copy _18, copy _19); -- assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, copy _18) -> [success: bb8, unwind unreachable]; -+ _20 = Lt(copy _16, copy _19); -+ assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, copy _16) -> [success: bb8, unwind unreachable]; +- _19 = Lt(copy _18, const 4_usize); +- assert(move _19, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _18) -> [success: bb8, unwind unreachable]; ++ _19 = Lt(copy _16, const 4_usize); ++ assert(move _19, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _16) -> [success: bb8, unwind unreachable]; } bb7: { diff --git a/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-unwind.diff b/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-unwind.diff index 3580c87c4699..7f768a9f834d 100644 --- a/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-unwind.diff +++ b/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.panic-unwind.diff @@ -18,8 +18,7 @@ let mut _15: !; let mut _17: i32; let _18: usize; - let mut _19: usize; - let mut _20: bool; + let mut _19: bool; scope 1 { debug sum => _1; let _2: [i32; 4]; @@ -92,11 +91,10 @@ StorageLive(_17); - StorageLive(_18); - _18 = copy _16; - _19 = Len(_2); -- _20 = Lt(copy _18, copy _19); -- assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, copy _18) -> [success: bb8, unwind continue]; -+ _20 = Lt(copy _16, copy _19); -+ assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, copy _16) -> [success: bb8, unwind continue]; +- _19 = Lt(copy _18, const 4_usize); +- assert(move _19, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _18) -> [success: bb8, unwind continue]; ++ _19 = Lt(copy _16, const 4_usize); ++ assert(move _19, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _16) -> [success: bb8, unwind continue]; } bb7: { diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff index a46daef435f3..0275d7e8a0d4 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff index 1a4e15b45faa..490ed4b55a1d 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff index a46daef435f3..0275d7e8a0d4 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff index 1a4e15b45faa..490ed4b55a1d 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u32; let mut _2: [u32; 4]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 4_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 4_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 4_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/dataflow-const-prop/array_index.rs b/tests/mir-opt/dataflow-const-prop/array_index.rs index e442ef99f79e..1aa8dcd28f4e 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.rs +++ b/tests/mir-opt/dataflow-const-prop/array_index.rs @@ -11,9 +11,10 @@ fn main() { // CHECK: [[array_lit]] = [const 0_u32, const 1_u32, const 2_u32, const 3_u32]; // CHECK-NOT: {{_.*}} = Len( + // CHECK-NOT: {{_.*}} = PtrMetadata( // CHECK-NOT: {{_.*}} = Lt( // CHECK-NOT: assert(move _ - // CHECK: {{_.*}} = const 4_usize; + // CHECK: {{_.*}} = const 2_usize; // CHECK: {{_.*}} = const true; // CHECK: assert(const true // CHECK: [[x]] = copy [[array_lit]][2 of 3]; diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff index b7ff0b671f7b..f0d59ef5923f 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff index af6e3626142f..959c3e75214f 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff index b7ff0b671f7b..f0d59ef5923f 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind unreachable]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff index af6e3626142f..959c3e75214f 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff @@ -6,8 +6,7 @@ let _1: u8; let mut _2: [u8; 5000]; let _3: usize; - let mut _4: usize; - let mut _5: bool; + let mut _4: bool; scope 1 { debug x => _1; } @@ -18,11 +17,9 @@ _2 = [const 0_u8; 5000]; StorageLive(_3); _3 = const 2_usize; -- _4 = Len(_2); -- _5 = Lt(copy _3, copy _4); -- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; -+ _4 = const 5000_usize; -+ _5 = const true; +- _4 = Lt(copy _3, const 5000_usize); +- assert(move _4, "index out of bounds: the length is {} but the index is {}", const 5000_usize, copy _3) -> [success: bb1, unwind continue]; ++ _4 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.rs b/tests/mir-opt/dataflow-const-prop/large_array_index.rs index e9f2fa2badf9..e490cfde2472 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.rs +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.rs @@ -10,7 +10,7 @@ fn main() { // CHECK: debug x => [[x:_.*]]; // CHECK: [[array_lit:_.*]] = [const 0_u8; 5000]; - // CHECK: {{_.*}} = const 5000_usize; + // CHECK: {{_.*}} = const 2_usize; // CHECK: {{_.*}} = const true; // CHECK: assert(const true // CHECK: [[x]] = copy [[array_lit]][2 of 3]; diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff index dfa541b1200d..618121ea6326 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind unreachable]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind unreachable]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff index 9ede3c5f7ac2..1788f58432b8 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind continue]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind continue]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff index dfa541b1200d..618121ea6326 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind unreachable]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind unreachable]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff index 9ede3c5f7ac2..1788f58432b8 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff @@ -7,8 +7,7 @@ let mut _2: u32; let mut _3: [u32; 8]; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: bool; scope 1 { debug x => _1; } @@ -20,11 +19,9 @@ _3 = [const 42_u32; 8]; StorageLive(_4); _4 = const 2_usize; -- _5 = Len(_3); -- _6 = Lt(copy _4, copy _5); -- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind continue]; -+ _5 = const 8_usize; -+ _6 = const true; +- _5 = Lt(copy _4, const 8_usize); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", const 8_usize, copy _4) -> [success: bb1, unwind continue]; ++ _5 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/dataflow-const-prop/repeat.rs b/tests/mir-opt/dataflow-const-prop/repeat.rs index 2067aa3d709e..1bc2cb82a606 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.rs +++ b/tests/mir-opt/dataflow-const-prop/repeat.rs @@ -9,8 +9,9 @@ fn main() { // CHECK: [[array_lit:_.*]] = [const 42_u32; 8]; // CHECK-NOT: {{_.*}} = Len( + // CHECK-NOT: {{_.*}} = PtrMetadata( // CHECK-NOT: {{_.*}} = Lt( - // CHECK: {{_.*}} = const 8_usize; + // CHECK: {{_.*}} = const 2_usize; // CHECK: {{_.*}} = const true; // CHECK: assert(const true diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff deleted file mode 100644 index e71992316dcf..000000000000 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `main` before DataflowConstProp -+ // MIR for `main` after DataflowConstProp - - fn main() -> () { - let mut _0: (); - let _1: u32; - let mut _2: &[u32]; - let mut _3: &[u32; 3]; - let _4: &[u32; 3]; - let _5: [u32; 3]; - let _6: usize; - let mut _7: usize; - let mut _8: bool; - let mut _10: &[u32]; - let _11: usize; - let mut _12: usize; - let mut _13: bool; - let mut _14: &[u32; 3]; - scope 1 { - debug local => _1; - let _9: u32; - scope 2 { - debug constant => _9; - } - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageLive(_3); - StorageLive(_4); - _14 = const main::promoted[0]; - _4 = copy _14; - _3 = copy _4; - _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); - StorageDead(_3); - StorageLive(_6); - _6 = const 1_usize; -- _7 = Len((*_2)); -- _8 = Lt(copy _6, copy _7); -- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; -+ _7 = const 3_usize; -+ _8 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; - } - - bb1: { -- _1 = copy (*_2)[_6]; -+ _1 = copy (*_2)[1 of 2]; - StorageDead(_6); - StorageDead(_4); - StorageDead(_2); - StorageLive(_9); - StorageLive(_10); - _10 = const main::SLICE; - StorageLive(_11); - _11 = const 1_usize; -- _12 = Len((*_10)); -- _13 = Lt(copy _11, copy _12); -- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind unreachable]; -+ _12 = const 3_usize; -+ _13 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; - } - - bb2: { -- _9 = copy (*_10)[_11]; -+ _9 = copy (*_10)[1 of 2]; - StorageDead(_11); - StorageDead(_10); - _0 = const (); - StorageDead(_9); - StorageDead(_1); - return; - } - } - diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff deleted file mode 100644 index 26de85957689..000000000000 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `main` before DataflowConstProp -+ // MIR for `main` after DataflowConstProp - - fn main() -> () { - let mut _0: (); - let _1: u32; - let mut _2: &[u32]; - let mut _3: &[u32; 3]; - let _4: &[u32; 3]; - let _5: [u32; 3]; - let _6: usize; - let mut _7: usize; - let mut _8: bool; - let mut _10: &[u32]; - let _11: usize; - let mut _12: usize; - let mut _13: bool; - let mut _14: &[u32; 3]; - scope 1 { - debug local => _1; - let _9: u32; - scope 2 { - debug constant => _9; - } - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageLive(_3); - StorageLive(_4); - _14 = const main::promoted[0]; - _4 = copy _14; - _3 = copy _4; - _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); - StorageDead(_3); - StorageLive(_6); - _6 = const 1_usize; -- _7 = Len((*_2)); -- _8 = Lt(copy _6, copy _7); -- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; -+ _7 = const 3_usize; -+ _8 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; - } - - bb1: { -- _1 = copy (*_2)[_6]; -+ _1 = copy (*_2)[1 of 2]; - StorageDead(_6); - StorageDead(_4); - StorageDead(_2); - StorageLive(_9); - StorageLive(_10); - _10 = const main::SLICE; - StorageLive(_11); - _11 = const 1_usize; -- _12 = Len((*_10)); -- _13 = Lt(copy _11, copy _12); -- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind continue]; -+ _12 = const 3_usize; -+ _13 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; - } - - bb2: { -- _9 = copy (*_10)[_11]; -+ _9 = copy (*_10)[1 of 2]; - StorageDead(_11); - StorageDead(_10); - _0 = const (); - StorageDead(_9); - StorageDead(_1); - return; - } - } - diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff deleted file mode 100644 index e71992316dcf..000000000000 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `main` before DataflowConstProp -+ // MIR for `main` after DataflowConstProp - - fn main() -> () { - let mut _0: (); - let _1: u32; - let mut _2: &[u32]; - let mut _3: &[u32; 3]; - let _4: &[u32; 3]; - let _5: [u32; 3]; - let _6: usize; - let mut _7: usize; - let mut _8: bool; - let mut _10: &[u32]; - let _11: usize; - let mut _12: usize; - let mut _13: bool; - let mut _14: &[u32; 3]; - scope 1 { - debug local => _1; - let _9: u32; - scope 2 { - debug constant => _9; - } - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageLive(_3); - StorageLive(_4); - _14 = const main::promoted[0]; - _4 = copy _14; - _3 = copy _4; - _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); - StorageDead(_3); - StorageLive(_6); - _6 = const 1_usize; -- _7 = Len((*_2)); -- _8 = Lt(copy _6, copy _7); -- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; -+ _7 = const 3_usize; -+ _8 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; - } - - bb1: { -- _1 = copy (*_2)[_6]; -+ _1 = copy (*_2)[1 of 2]; - StorageDead(_6); - StorageDead(_4); - StorageDead(_2); - StorageLive(_9); - StorageLive(_10); - _10 = const main::SLICE; - StorageLive(_11); - _11 = const 1_usize; -- _12 = Len((*_10)); -- _13 = Lt(copy _11, copy _12); -- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind unreachable]; -+ _12 = const 3_usize; -+ _13 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; - } - - bb2: { -- _9 = copy (*_10)[_11]; -+ _9 = copy (*_10)[1 of 2]; - StorageDead(_11); - StorageDead(_10); - _0 = const (); - StorageDead(_9); - StorageDead(_1); - return; - } - } - diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff deleted file mode 100644 index 26de85957689..000000000000 --- a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `main` before DataflowConstProp -+ // MIR for `main` after DataflowConstProp - - fn main() -> () { - let mut _0: (); - let _1: u32; - let mut _2: &[u32]; - let mut _3: &[u32; 3]; - let _4: &[u32; 3]; - let _5: [u32; 3]; - let _6: usize; - let mut _7: usize; - let mut _8: bool; - let mut _10: &[u32]; - let _11: usize; - let mut _12: usize; - let mut _13: bool; - let mut _14: &[u32; 3]; - scope 1 { - debug local => _1; - let _9: u32; - scope 2 { - debug constant => _9; - } - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - StorageLive(_3); - StorageLive(_4); - _14 = const main::promoted[0]; - _4 = copy _14; - _3 = copy _4; - _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); - StorageDead(_3); - StorageLive(_6); - _6 = const 1_usize; -- _7 = Len((*_2)); -- _8 = Lt(copy _6, copy _7); -- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; -+ _7 = const 3_usize; -+ _8 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; - } - - bb1: { -- _1 = copy (*_2)[_6]; -+ _1 = copy (*_2)[1 of 2]; - StorageDead(_6); - StorageDead(_4); - StorageDead(_2); - StorageLive(_9); - StorageLive(_10); - _10 = const main::SLICE; - StorageLive(_11); - _11 = const 1_usize; -- _12 = Len((*_10)); -- _13 = Lt(copy _11, copy _12); -- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind continue]; -+ _12 = const 3_usize; -+ _13 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; - } - - bb2: { -- _9 = copy (*_10)[_11]; -+ _9 = copy (*_10)[1 of 2]; - StorageDead(_11); - StorageDead(_10); - _0 = const (); - StorageDead(_9); - StorageDead(_1); - return; - } - } - diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs deleted file mode 100644 index e0e68f9fde54..000000000000 --- a/tests/mir-opt/dataflow-const-prop/slice_len.rs +++ /dev/null @@ -1,34 +0,0 @@ -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ test-mir-pass: DataflowConstProp -//@ compile-flags: -Zmir-enable-passes=+InstSimplify-after-simplifycfg -// EMIT_MIR_FOR_EACH_BIT_WIDTH - -// EMIT_MIR slice_len.main.DataflowConstProp.diff - -// CHECK-LABEL: fn main( -fn main() { - // CHECK: debug local => [[local:_.*]]; - // CHECK: debug constant => [[constant:_.*]]; - - // CHECK-NOT: {{_.*}} = Len( - // CHECK-NOT: {{_.*}} = Lt( - // CHECK-NOT: assert(move _ - // CHECK: {{_.*}} = const 3_usize; - // CHECK: {{_.*}} = const true; - // CHECK: assert(const true, - - // CHECK: [[local]] = copy (*{{_.*}})[1 of 2]; - let local = (&[1u32, 2, 3] as &[u32])[1]; - - // CHECK-NOT: {{_.*}} = Len( - // CHECK-NOT: {{_.*}} = Lt( - // CHECK-NOT: assert(move _ - const SLICE: &[u32] = &[1, 2, 3]; - // CHECK: {{_.*}} = const 3_usize; - // CHECK: {{_.*}} = const true; - // CHECK: assert(const true, - - // CHECK-NOT: [[constant]] = {{copy|move}} (*{{_.*}})[_ - // CHECK: [[constant]] = copy (*{{_.*}})[1 of 2]; - let constant = SLICE[1]; -} diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff index 3f052ee19fdf..183b4d2599f5 100644 --- a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-abort.diff @@ -53,7 +53,7 @@ StorageLive(_8); - _8 = copy _2; + _8 = const usize::MAX; - _9 = Len((*_1)); + _9 = PtrMetadata(copy _1); - _10 = Lt(copy _8, copy _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb3, unwind unreachable]; + _10 = Lt(const usize::MAX, copy _9); @@ -72,7 +72,7 @@ StorageDead(_5); StorageLive(_11); _11 = const 0_usize; - _12 = Len((*_1)); + _12 = PtrMetadata(copy _1); - _13 = Lt(copy _11, copy _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb5, unwind unreachable]; + _13 = Lt(const 0_usize, copy _12); diff --git a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff index 84b738c7804e..03e8aa3bd9b9 100644 --- a/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.constant_index_overflow.GVN.panic-unwind.diff @@ -53,7 +53,7 @@ StorageLive(_8); - _8 = copy _2; + _8 = const usize::MAX; - _9 = Len((*_1)); + _9 = PtrMetadata(copy _1); - _10 = Lt(copy _8, copy _9); - assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb3, unwind continue]; + _10 = Lt(const usize::MAX, copy _9); @@ -72,7 +72,7 @@ StorageDead(_5); StorageLive(_11); _11 = const 0_usize; - _12 = Len((*_1)); + _12 = PtrMetadata(copy _1); - _13 = Lt(copy _11, copy _12); - assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb5, unwind continue]; + _13 = Lt(const 0_usize, copy _12); diff --git a/tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-abort.diff b/tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-abort.diff new file mode 100644 index 000000000000..4b077f580f10 --- /dev/null +++ b/tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-abort.diff @@ -0,0 +1,72 @@ +- // MIR for `dedup_multiple_bounds_checks_lengths` before GVN ++ // MIR for `dedup_multiple_bounds_checks_lengths` after GVN + + fn dedup_multiple_bounds_checks_lengths(_1: &[i32]) -> [i32; 3] { + debug x => _1; + let mut _0: [i32; 3]; + let mut _2: i32; + let _3: usize; + let mut _4: usize; + let mut _5: bool; + let mut _6: i32; + let _7: usize; + let mut _8: usize; + let mut _9: bool; + let mut _10: i32; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 42_usize; + _4 = PtrMetadata(copy _1); +- _5 = Lt(copy _3, copy _4); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; ++ _5 = Lt(const 42_usize, copy _4); ++ assert(move _5, "index out of bounds: the length is {} but the index is {}", copy _4, const 42_usize) -> [success: bb1, unwind unreachable]; + } + + bb1: { +- _2 = copy (*_1)[_3]; ++ _2 = copy (*_1)[42 of 43]; + StorageLive(_6); + StorageLive(_7); + _7 = const 13_usize; +- _8 = PtrMetadata(copy _1); +- _9 = Lt(copy _7, copy _8); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind unreachable]; ++ _8 = copy _4; ++ _9 = Lt(const 13_usize, copy _4); ++ assert(move _9, "index out of bounds: the length is {} but the index is {}", copy _4, const 13_usize) -> [success: bb2, unwind unreachable]; + } + + bb2: { +- _6 = copy (*_1)[_7]; ++ _6 = copy (*_1)[13 of 14]; + StorageLive(_10); + StorageLive(_11); + _11 = const 7_usize; +- _12 = PtrMetadata(copy _1); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb3, unwind unreachable]; ++ _12 = copy _4; ++ _13 = Lt(const 7_usize, copy _4); ++ assert(move _13, "index out of bounds: the length is {} but the index is {}", copy _4, const 7_usize) -> [success: bb3, unwind unreachable]; + } + + bb3: { +- _10 = copy (*_1)[_11]; ++ _10 = copy (*_1)[7 of 8]; + _0 = [move _2, move _6, move _10]; + StorageDead(_10); + StorageDead(_6); + StorageDead(_2); + StorageDead(_11); + StorageDead(_7); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-unwind.diff b/tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-unwind.diff new file mode 100644 index 000000000000..87e69d440069 --- /dev/null +++ b/tests/mir-opt/gvn.dedup_multiple_bounds_checks_lengths.GVN.panic-unwind.diff @@ -0,0 +1,72 @@ +- // MIR for `dedup_multiple_bounds_checks_lengths` before GVN ++ // MIR for `dedup_multiple_bounds_checks_lengths` after GVN + + fn dedup_multiple_bounds_checks_lengths(_1: &[i32]) -> [i32; 3] { + debug x => _1; + let mut _0: [i32; 3]; + let mut _2: i32; + let _3: usize; + let mut _4: usize; + let mut _5: bool; + let mut _6: i32; + let _7: usize; + let mut _8: usize; + let mut _9: bool; + let mut _10: i32; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 42_usize; + _4 = PtrMetadata(copy _1); +- _5 = Lt(copy _3, copy _4); +- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; ++ _5 = Lt(const 42_usize, copy _4); ++ assert(move _5, "index out of bounds: the length is {} but the index is {}", copy _4, const 42_usize) -> [success: bb1, unwind continue]; + } + + bb1: { +- _2 = copy (*_1)[_3]; ++ _2 = copy (*_1)[42 of 43]; + StorageLive(_6); + StorageLive(_7); + _7 = const 13_usize; +- _8 = PtrMetadata(copy _1); +- _9 = Lt(copy _7, copy _8); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind continue]; ++ _8 = copy _4; ++ _9 = Lt(const 13_usize, copy _4); ++ assert(move _9, "index out of bounds: the length is {} but the index is {}", copy _4, const 13_usize) -> [success: bb2, unwind continue]; + } + + bb2: { +- _6 = copy (*_1)[_7]; ++ _6 = copy (*_1)[13 of 14]; + StorageLive(_10); + StorageLive(_11); + _11 = const 7_usize; +- _12 = PtrMetadata(copy _1); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb3, unwind continue]; ++ _12 = copy _4; ++ _13 = Lt(const 7_usize, copy _4); ++ assert(move _13, "index out of bounds: the length is {} but the index is {}", copy _4, const 7_usize) -> [success: bb3, unwind continue]; + } + + bb3: { +- _10 = copy (*_1)[_11]; ++ _10 = copy (*_1)[7 of 8]; + _0 = [move _2, move _6, move _10]; + StorageDead(_10); + StorageDead(_6); + StorageDead(_2); + StorageDead(_11); + StorageDead(_7); + StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/gvn.repeated_index.GVN.panic-abort.diff b/tests/mir-opt/gvn.repeated_index.GVN.panic-abort.diff index d4b22d05f6c7..7f44176b7568 100644 --- a/tests/mir-opt/gvn.repeated_index.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.repeated_index.GVN.panic-abort.diff @@ -10,13 +10,11 @@ let _5: (); let mut _6: T; let _7: usize; - let mut _8: usize; - let mut _9: bool; - let _10: (); - let mut _11: T; - let _12: usize; - let mut _13: usize; - let mut _14: bool; + let mut _8: bool; + let _9: (); + let mut _10: T; + let _11: usize; + let mut _12: bool; scope 1 { debug a => _3; } @@ -32,12 +30,10 @@ StorageLive(_6); StorageLive(_7); _7 = const 0_usize; -- _8 = Len(_3); -- _9 = Lt(copy _7, copy _8); -- assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb1, unwind unreachable]; -+ _8 = const N; -+ _9 = Lt(const 0_usize, const N); -+ assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb1, unwind unreachable]; +- _8 = Lt(copy _7, const N); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", const N, copy _7) -> [success: bb1, unwind unreachable]; ++ _8 = Lt(const 0_usize, const N); ++ assert(move _8, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb1, unwind unreachable]; } bb1: { @@ -51,29 +47,27 @@ StorageDead(_6); StorageDead(_7); StorageDead(_5); + StorageLive(_9); StorageLive(_10); StorageLive(_11); - StorageLive(_12); - _12 = copy _2; -- _13 = Len(_3); -- _14 = Lt(copy _12, copy _13); -- assert(move _14, "index out of bounds: the length is {} but the index is {}", move _13, copy _12) -> [success: bb3, unwind unreachable]; -+ _13 = const N; -+ _14 = Lt(copy _2, const N); -+ assert(move _14, "index out of bounds: the length is {} but the index is {}", const N, copy _2) -> [success: bb3, unwind unreachable]; + _11 = copy _2; +- _12 = Lt(copy _11, const N); +- assert(move _12, "index out of bounds: the length is {} but the index is {}", const N, copy _11) -> [success: bb3, unwind unreachable]; ++ _12 = Lt(copy _2, const N); ++ assert(move _12, "index out of bounds: the length is {} but the index is {}", const N, copy _2) -> [success: bb3, unwind unreachable]; } bb3: { -- _11 = copy _3[_12]; -- _10 = opaque::(move _11) -> [return: bb4, unwind unreachable]; -+ _11 = copy _1; -+ _10 = opaque::(copy _1) -> [return: bb4, unwind unreachable]; +- _10 = copy _3[_11]; +- _9 = opaque::(move _10) -> [return: bb4, unwind unreachable]; ++ _10 = copy _1; ++ _9 = opaque::(copy _1) -> [return: bb4, unwind unreachable]; } bb4: { - StorageDead(_11); - StorageDead(_12); StorageDead(_10); + StorageDead(_11); + StorageDead(_9); _0 = const (); StorageDead(_3); return; diff --git a/tests/mir-opt/gvn.repeated_index.GVN.panic-unwind.diff b/tests/mir-opt/gvn.repeated_index.GVN.panic-unwind.diff index 708c0f92e542..d34882d725f4 100644 --- a/tests/mir-opt/gvn.repeated_index.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.repeated_index.GVN.panic-unwind.diff @@ -10,13 +10,11 @@ let _5: (); let mut _6: T; let _7: usize; - let mut _8: usize; - let mut _9: bool; - let _10: (); - let mut _11: T; - let _12: usize; - let mut _13: usize; - let mut _14: bool; + let mut _8: bool; + let _9: (); + let mut _10: T; + let _11: usize; + let mut _12: bool; scope 1 { debug a => _3; } @@ -32,12 +30,10 @@ StorageLive(_6); StorageLive(_7); _7 = const 0_usize; -- _8 = Len(_3); -- _9 = Lt(copy _7, copy _8); -- assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb1, unwind continue]; -+ _8 = const N; -+ _9 = Lt(const 0_usize, const N); -+ assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb1, unwind continue]; +- _8 = Lt(copy _7, const N); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", const N, copy _7) -> [success: bb1, unwind continue]; ++ _8 = Lt(const 0_usize, const N); ++ assert(move _8, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb1, unwind continue]; } bb1: { @@ -51,29 +47,27 @@ StorageDead(_6); StorageDead(_7); StorageDead(_5); + StorageLive(_9); StorageLive(_10); StorageLive(_11); - StorageLive(_12); - _12 = copy _2; -- _13 = Len(_3); -- _14 = Lt(copy _12, copy _13); -- assert(move _14, "index out of bounds: the length is {} but the index is {}", move _13, copy _12) -> [success: bb3, unwind continue]; -+ _13 = const N; -+ _14 = Lt(copy _2, const N); -+ assert(move _14, "index out of bounds: the length is {} but the index is {}", const N, copy _2) -> [success: bb3, unwind continue]; + _11 = copy _2; +- _12 = Lt(copy _11, const N); +- assert(move _12, "index out of bounds: the length is {} but the index is {}", const N, copy _11) -> [success: bb3, unwind continue]; ++ _12 = Lt(copy _2, const N); ++ assert(move _12, "index out of bounds: the length is {} but the index is {}", const N, copy _2) -> [success: bb3, unwind continue]; } bb3: { -- _11 = copy _3[_12]; -- _10 = opaque::(move _11) -> [return: bb4, unwind continue]; -+ _11 = copy _1; -+ _10 = opaque::(copy _1) -> [return: bb4, unwind continue]; +- _10 = copy _3[_11]; +- _9 = opaque::(move _10) -> [return: bb4, unwind continue]; ++ _10 = copy _1; ++ _9 = opaque::(copy _1) -> [return: bb4, unwind continue]; } bb4: { - StorageDead(_11); - StorageDead(_12); StorageDead(_10); + StorageDead(_11); + StorageDead(_9); _0 = const (); StorageDead(_3); return; diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 10d1ccfdece0..c895a5792595 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -835,6 +835,25 @@ fn array_len(x: &mut [i32; 42]) -> usize { std::intrinsics::ptr_metadata(x) } +// Check that we only load the length once, rather than all 3 times. +fn dedup_multiple_bounds_checks_lengths(x: &[i32]) -> [i32; 3] { + // CHECK-LABEL: fn dedup_multiple_bounds_checks_lengths + // CHECK: [[LEN:_.+]] = PtrMetadata(copy _1); + // CHECK: Lt(const 42_usize, copy [[LEN]]); + // CHECK: assert{{.+}}copy [[LEN]] + // CHECK: [[A:_.+]] = copy (*_1)[42 of 43]; + // CHECK-NOT: PtrMetadata + // CHECK: Lt(const 13_usize, copy [[LEN]]); + // CHECK: assert{{.+}}copy [[LEN]] + // CHECK: [[B:_.+]] = copy (*_1)[13 of 14]; + // CHECK-NOT: PtrMetadata + // CHECK: Lt(const 7_usize, copy [[LEN]]); + // CHECK: assert{{.+}}copy [[LEN]] + // CHECK: [[C:_.+]] = copy (*_1)[7 of 8]; + // CHECK: _0 = [move [[A]], move [[B]], move [[C]]] + [x[42], x[13], x[7]] +} + #[custom_mir(dialect = "runtime")] fn generic_cast_metadata(ps: *const [T], pa: *const A, pb: *const B) { // CHECK-LABEL: fn generic_cast_metadata @@ -1109,6 +1128,7 @@ enum Never {} // EMIT_MIR gvn.casts_before_aggregate_raw_ptr.GVN.diff // EMIT_MIR gvn.manual_slice_mut_len.GVN.diff // EMIT_MIR gvn.array_len.GVN.diff +// EMIT_MIR gvn.dedup_multiple_bounds_checks_lengths.GVN.diff // EMIT_MIR gvn.generic_cast_metadata.GVN.diff // EMIT_MIR gvn.cast_pointer_eq.GVN.diff // EMIT_MIR gvn.aggregate_struct_then_transmute.GVN.diff diff --git a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff index 6b6152c1117e..1b305e746f5e 100644 --- a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-abort.diff @@ -10,62 +10,60 @@ let mut _6: &i32; let _7: &i32; let _8: usize; - let mut _9: usize; - let mut _10: bool; - let mut _12: *const dyn std::marker::Send; - let _13: &dyn std::marker::Send; - let mut _14: &i32; - let _15: &i32; - let _16: usize; - let mut _17: usize; + let mut _9: bool; + let mut _11: *const dyn std::marker::Send; + let _12: &dyn std::marker::Send; + let mut _13: &i32; + let _14: &i32; + let _15: usize; + let mut _16: bool; + let _17: (); let mut _18: bool; - let _19: (); - let mut _20: bool; + let mut _19: *const dyn std::marker::Send; + let mut _20: *const dyn std::marker::Send; let mut _21: *const dyn std::marker::Send; - let mut _22: *const dyn std::marker::Send; - let mut _23: *const dyn std::marker::Send; - let _24: (); - let mut _25: bool; + let _22: (); + let mut _23: bool; + let mut _24: *const dyn std::marker::Send; + let mut _25: *const dyn std::marker::Send; let mut _26: *const dyn std::marker::Send; - let mut _27: *const dyn std::marker::Send; - let mut _28: *const dyn std::marker::Send; - let _29: (); - let mut _30: bool; + let _27: (); + let mut _28: bool; + let mut _29: *const dyn std::marker::Send; + let mut _30: *const dyn std::marker::Send; let mut _31: *const dyn std::marker::Send; - let mut _32: *const dyn std::marker::Send; - let mut _33: *const dyn std::marker::Send; - let _34: (); - let mut _35: bool; + let _32: (); + let mut _33: bool; + let mut _34: *const dyn std::marker::Send; + let mut _35: *const dyn std::marker::Send; let mut _36: *const dyn std::marker::Send; - let mut _37: *const dyn std::marker::Send; - let mut _38: *const dyn std::marker::Send; - let _39: (); - let mut _40: bool; + let _37: (); + let mut _38: bool; + let mut _39: *const dyn std::marker::Send; + let mut _40: *const dyn std::marker::Send; let mut _41: *const dyn std::marker::Send; - let mut _42: *const dyn std::marker::Send; - let mut _43: *const dyn std::marker::Send; - let _44: (); - let mut _45: bool; + let _42: (); + let mut _43: bool; + let mut _44: *const dyn std::marker::Send; + let mut _45: *const dyn std::marker::Send; let mut _46: *const dyn std::marker::Send; - let mut _47: *const dyn std::marker::Send; - let mut _48: *const dyn std::marker::Send; - let mut _49: &[i32; 2]; + let mut _47: &[i32; 2]; scope 1 { debug slice => _1; let _3: *const dyn std::marker::Send; scope 2 { debug a => _3; - let _11: *const dyn std::marker::Send; + let _10: *const dyn std::marker::Send; scope 3 { - debug b => _11; + debug b => _10; } } } bb0: { StorageLive(_1); - _49 = const wide_ptr_same_provenance::promoted[0]; - _1 = &(*_49); + _47 = const wide_ptr_same_provenance::promoted[0]; + _1 = &(*_47); StorageLive(_3); - StorageLive(_4); + nop; @@ -74,11 +72,9 @@ StorageLive(_7); StorageLive(_8); _8 = const 0_usize; -- _9 = Len((*_1)); -- _10 = Lt(copy _8, copy _9); -- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb1, unwind unreachable]; -+ _9 = const 2_usize; -+ _10 = const true; +- _9 = Lt(copy _8, const 2_usize); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _8) -> [success: bb1, unwind unreachable]; ++ _9 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 2_usize, const 0_usize) -> [success: bb1, unwind unreachable]; } @@ -95,170 +91,168 @@ + nop; StorageDead(_7); StorageDead(_5); - StorageLive(_11); -- StorageLive(_12); + StorageLive(_10); +- StorageLive(_11); + nop; + StorageLive(_12); StorageLive(_13); StorageLive(_14); StorageLive(_15); - StorageLive(_16); - _16 = const 1_usize; -- _17 = Len((*_1)); -- _18 = Lt(copy _16, copy _17); -- assert(move _18, "index out of bounds: the length is {} but the index is {}", move _17, copy _16) -> [success: bb2, unwind unreachable]; -+ _17 = const 2_usize; -+ _18 = const true; + _15 = const 1_usize; +- _16 = Lt(copy _15, const 2_usize); +- assert(move _16, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _15) -> [success: bb2, unwind unreachable]; ++ _16 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 2_usize, const 1_usize) -> [success: bb2, unwind unreachable]; } bb2: { -- _15 = &(*_1)[_16]; -+ _15 = &(*_1)[1 of 2]; - _14 = &(*_15); - _13 = move _14 as &dyn std::marker::Send (PointerCoercion(Unsize, AsCast)); - StorageDead(_14); - _12 = &raw const (*_13); -- _11 = move _12 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -- StorageDead(_12); -+ _11 = copy _12; -+ nop; - StorageDead(_15); +- _14 = &(*_1)[_15]; ++ _14 = &(*_1)[1 of 2]; + _13 = &(*_14); + _12 = move _13 as &dyn std::marker::Send (PointerCoercion(Unsize, AsCast)); StorageDead(_13); + _11 = &raw const (*_12); +- _10 = move _11 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); +- StorageDead(_11); ++ _10 = copy _11; ++ nop; + StorageDead(_14); + StorageDead(_12); + StorageLive(_17); + StorageLive(_18); StorageLive(_19); +- _19 = copy _3; ++ _19 = copy _4; StorageLive(_20); StorageLive(_21); -- _21 = copy _3; -+ _21 = copy _4; - StorageLive(_22); - StorageLive(_23); -- _23 = copy _11; -- _22 = move _23 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _23 = copy _12; -+ _22 = copy _12; - StorageDead(_23); -- _20 = Eq(move _21, move _22); -+ _20 = Eq(copy _4, copy _12); - StorageDead(_22); +- _21 = copy _10; +- _20 = move _21 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _21 = copy _11; ++ _20 = copy _11; StorageDead(_21); - _19 = opaque::(move _20) -> [return: bb3, unwind unreachable]; +- _18 = Eq(move _19, move _20); ++ _18 = Eq(copy _4, copy _11); + StorageDead(_20); + StorageDead(_19); + _17 = opaque::(move _18) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_20); - StorageDead(_19); + StorageDead(_18); + StorageDead(_17); + StorageLive(_22); + StorageLive(_23); StorageLive(_24); +- _24 = copy _3; ++ _24 = copy _4; StorageLive(_25); StorageLive(_26); -- _26 = copy _3; -+ _26 = copy _4; - StorageLive(_27); - StorageLive(_28); -- _28 = copy _11; -- _27 = move _28 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _28 = copy _12; -+ _27 = copy _12; - StorageDead(_28); -- _25 = Ne(move _26, move _27); -+ _25 = Ne(copy _4, copy _12); - StorageDead(_27); +- _26 = copy _10; +- _25 = move _26 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _26 = copy _11; ++ _25 = copy _11; StorageDead(_26); - _24 = opaque::(move _25) -> [return: bb4, unwind unreachable]; +- _23 = Ne(move _24, move _25); ++ _23 = Ne(copy _4, copy _11); + StorageDead(_25); + StorageDead(_24); + _22 = opaque::(move _23) -> [return: bb4, unwind unreachable]; } bb4: { - StorageDead(_25); - StorageDead(_24); + StorageDead(_23); + StorageDead(_22); + StorageLive(_27); + StorageLive(_28); StorageLive(_29); +- _29 = copy _3; ++ _29 = copy _4; StorageLive(_30); StorageLive(_31); -- _31 = copy _3; -+ _31 = copy _4; - StorageLive(_32); - StorageLive(_33); -- _33 = copy _11; -- _32 = move _33 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _33 = copy _12; -+ _32 = copy _12; - StorageDead(_33); -- _30 = Lt(move _31, move _32); -+ _30 = Lt(copy _4, copy _12); - StorageDead(_32); +- _31 = copy _10; +- _30 = move _31 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _31 = copy _11; ++ _30 = copy _11; StorageDead(_31); - _29 = opaque::(move _30) -> [return: bb5, unwind unreachable]; +- _28 = Lt(move _29, move _30); ++ _28 = Lt(copy _4, copy _11); + StorageDead(_30); + StorageDead(_29); + _27 = opaque::(move _28) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_30); - StorageDead(_29); + StorageDead(_28); + StorageDead(_27); + StorageLive(_32); + StorageLive(_33); StorageLive(_34); +- _34 = copy _3; ++ _34 = copy _4; StorageLive(_35); StorageLive(_36); -- _36 = copy _3; -+ _36 = copy _4; - StorageLive(_37); - StorageLive(_38); -- _38 = copy _11; -- _37 = move _38 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _38 = copy _12; -+ _37 = copy _12; - StorageDead(_38); -- _35 = Le(move _36, move _37); -+ _35 = Le(copy _4, copy _12); - StorageDead(_37); +- _36 = copy _10; +- _35 = move _36 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _36 = copy _11; ++ _35 = copy _11; StorageDead(_36); - _34 = opaque::(move _35) -> [return: bb6, unwind unreachable]; +- _33 = Le(move _34, move _35); ++ _33 = Le(copy _4, copy _11); + StorageDead(_35); + StorageDead(_34); + _32 = opaque::(move _33) -> [return: bb6, unwind unreachable]; } bb6: { - StorageDead(_35); - StorageDead(_34); + StorageDead(_33); + StorageDead(_32); + StorageLive(_37); + StorageLive(_38); StorageLive(_39); +- _39 = copy _3; ++ _39 = copy _4; StorageLive(_40); StorageLive(_41); -- _41 = copy _3; -+ _41 = copy _4; - StorageLive(_42); - StorageLive(_43); -- _43 = copy _11; -- _42 = move _43 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _43 = copy _12; -+ _42 = copy _12; - StorageDead(_43); -- _40 = Gt(move _41, move _42); -+ _40 = Gt(copy _4, copy _12); - StorageDead(_42); +- _41 = copy _10; +- _40 = move _41 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _41 = copy _11; ++ _40 = copy _11; StorageDead(_41); - _39 = opaque::(move _40) -> [return: bb7, unwind unreachable]; +- _38 = Gt(move _39, move _40); ++ _38 = Gt(copy _4, copy _11); + StorageDead(_40); + StorageDead(_39); + _37 = opaque::(move _38) -> [return: bb7, unwind unreachable]; } bb7: { - StorageDead(_40); - StorageDead(_39); + StorageDead(_38); + StorageDead(_37); + StorageLive(_42); + StorageLive(_43); StorageLive(_44); +- _44 = copy _3; ++ _44 = copy _4; StorageLive(_45); StorageLive(_46); -- _46 = copy _3; -+ _46 = copy _4; - StorageLive(_47); - StorageLive(_48); -- _48 = copy _11; -- _47 = move _48 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _48 = copy _12; -+ _47 = copy _12; - StorageDead(_48); -- _45 = Ge(move _46, move _47); -+ _45 = Ge(copy _4, copy _12); - StorageDead(_47); +- _46 = copy _10; +- _45 = move _46 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _46 = copy _11; ++ _45 = copy _11; StorageDead(_46); - _44 = opaque::(move _45) -> [return: bb8, unwind unreachable]; +- _43 = Ge(move _44, move _45); ++ _43 = Ge(copy _4, copy _11); + StorageDead(_45); + StorageDead(_44); + _42 = opaque::(move _43) -> [return: bb8, unwind unreachable]; } bb8: { - StorageDead(_45); - StorageDead(_44); + StorageDead(_43); + StorageDead(_42); _0 = const (); - StorageDead(_16); - StorageDead(_11); + StorageDead(_15); + StorageDead(_10); StorageDead(_8); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff index 093c1ec6ce37..e418ecf25bd4 100644 --- a/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.wide_ptr_same_provenance.GVN.panic-unwind.diff @@ -10,62 +10,60 @@ let mut _6: &i32; let _7: &i32; let _8: usize; - let mut _9: usize; - let mut _10: bool; - let mut _12: *const dyn std::marker::Send; - let _13: &dyn std::marker::Send; - let mut _14: &i32; - let _15: &i32; - let _16: usize; - let mut _17: usize; + let mut _9: bool; + let mut _11: *const dyn std::marker::Send; + let _12: &dyn std::marker::Send; + let mut _13: &i32; + let _14: &i32; + let _15: usize; + let mut _16: bool; + let _17: (); let mut _18: bool; - let _19: (); - let mut _20: bool; + let mut _19: *const dyn std::marker::Send; + let mut _20: *const dyn std::marker::Send; let mut _21: *const dyn std::marker::Send; - let mut _22: *const dyn std::marker::Send; - let mut _23: *const dyn std::marker::Send; - let _24: (); - let mut _25: bool; + let _22: (); + let mut _23: bool; + let mut _24: *const dyn std::marker::Send; + let mut _25: *const dyn std::marker::Send; let mut _26: *const dyn std::marker::Send; - let mut _27: *const dyn std::marker::Send; - let mut _28: *const dyn std::marker::Send; - let _29: (); - let mut _30: bool; + let _27: (); + let mut _28: bool; + let mut _29: *const dyn std::marker::Send; + let mut _30: *const dyn std::marker::Send; let mut _31: *const dyn std::marker::Send; - let mut _32: *const dyn std::marker::Send; - let mut _33: *const dyn std::marker::Send; - let _34: (); - let mut _35: bool; + let _32: (); + let mut _33: bool; + let mut _34: *const dyn std::marker::Send; + let mut _35: *const dyn std::marker::Send; let mut _36: *const dyn std::marker::Send; - let mut _37: *const dyn std::marker::Send; - let mut _38: *const dyn std::marker::Send; - let _39: (); - let mut _40: bool; + let _37: (); + let mut _38: bool; + let mut _39: *const dyn std::marker::Send; + let mut _40: *const dyn std::marker::Send; let mut _41: *const dyn std::marker::Send; - let mut _42: *const dyn std::marker::Send; - let mut _43: *const dyn std::marker::Send; - let _44: (); - let mut _45: bool; + let _42: (); + let mut _43: bool; + let mut _44: *const dyn std::marker::Send; + let mut _45: *const dyn std::marker::Send; let mut _46: *const dyn std::marker::Send; - let mut _47: *const dyn std::marker::Send; - let mut _48: *const dyn std::marker::Send; - let mut _49: &[i32; 2]; + let mut _47: &[i32; 2]; scope 1 { debug slice => _1; let _3: *const dyn std::marker::Send; scope 2 { debug a => _3; - let _11: *const dyn std::marker::Send; + let _10: *const dyn std::marker::Send; scope 3 { - debug b => _11; + debug b => _10; } } } bb0: { StorageLive(_1); - _49 = const wide_ptr_same_provenance::promoted[0]; - _1 = &(*_49); + _47 = const wide_ptr_same_provenance::promoted[0]; + _1 = &(*_47); StorageLive(_3); - StorageLive(_4); + nop; @@ -74,11 +72,9 @@ StorageLive(_7); StorageLive(_8); _8 = const 0_usize; -- _9 = Len((*_1)); -- _10 = Lt(copy _8, copy _9); -- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb1, unwind continue]; -+ _9 = const 2_usize; -+ _10 = const true; +- _9 = Lt(copy _8, const 2_usize); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _8) -> [success: bb1, unwind continue]; ++ _9 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 2_usize, const 0_usize) -> [success: bb1, unwind continue]; } @@ -95,170 +91,168 @@ + nop; StorageDead(_7); StorageDead(_5); - StorageLive(_11); -- StorageLive(_12); + StorageLive(_10); +- StorageLive(_11); + nop; + StorageLive(_12); StorageLive(_13); StorageLive(_14); StorageLive(_15); - StorageLive(_16); - _16 = const 1_usize; -- _17 = Len((*_1)); -- _18 = Lt(copy _16, copy _17); -- assert(move _18, "index out of bounds: the length is {} but the index is {}", move _17, copy _16) -> [success: bb2, unwind continue]; -+ _17 = const 2_usize; -+ _18 = const true; + _15 = const 1_usize; +- _16 = Lt(copy _15, const 2_usize); +- assert(move _16, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _15) -> [success: bb2, unwind continue]; ++ _16 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 2_usize, const 1_usize) -> [success: bb2, unwind continue]; } bb2: { -- _15 = &(*_1)[_16]; -+ _15 = &(*_1)[1 of 2]; - _14 = &(*_15); - _13 = move _14 as &dyn std::marker::Send (PointerCoercion(Unsize, AsCast)); - StorageDead(_14); - _12 = &raw const (*_13); -- _11 = move _12 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -- StorageDead(_12); -+ _11 = copy _12; -+ nop; - StorageDead(_15); +- _14 = &(*_1)[_15]; ++ _14 = &(*_1)[1 of 2]; + _13 = &(*_14); + _12 = move _13 as &dyn std::marker::Send (PointerCoercion(Unsize, AsCast)); StorageDead(_13); + _11 = &raw const (*_12); +- _10 = move _11 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); +- StorageDead(_11); ++ _10 = copy _11; ++ nop; + StorageDead(_14); + StorageDead(_12); + StorageLive(_17); + StorageLive(_18); StorageLive(_19); +- _19 = copy _3; ++ _19 = copy _4; StorageLive(_20); StorageLive(_21); -- _21 = copy _3; -+ _21 = copy _4; - StorageLive(_22); - StorageLive(_23); -- _23 = copy _11; -- _22 = move _23 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _23 = copy _12; -+ _22 = copy _12; - StorageDead(_23); -- _20 = Eq(move _21, move _22); -+ _20 = Eq(copy _4, copy _12); - StorageDead(_22); +- _21 = copy _10; +- _20 = move _21 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _21 = copy _11; ++ _20 = copy _11; StorageDead(_21); - _19 = opaque::(move _20) -> [return: bb3, unwind continue]; +- _18 = Eq(move _19, move _20); ++ _18 = Eq(copy _4, copy _11); + StorageDead(_20); + StorageDead(_19); + _17 = opaque::(move _18) -> [return: bb3, unwind continue]; } bb3: { - StorageDead(_20); - StorageDead(_19); + StorageDead(_18); + StorageDead(_17); + StorageLive(_22); + StorageLive(_23); StorageLive(_24); +- _24 = copy _3; ++ _24 = copy _4; StorageLive(_25); StorageLive(_26); -- _26 = copy _3; -+ _26 = copy _4; - StorageLive(_27); - StorageLive(_28); -- _28 = copy _11; -- _27 = move _28 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _28 = copy _12; -+ _27 = copy _12; - StorageDead(_28); -- _25 = Ne(move _26, move _27); -+ _25 = Ne(copy _4, copy _12); - StorageDead(_27); +- _26 = copy _10; +- _25 = move _26 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _26 = copy _11; ++ _25 = copy _11; StorageDead(_26); - _24 = opaque::(move _25) -> [return: bb4, unwind continue]; +- _23 = Ne(move _24, move _25); ++ _23 = Ne(copy _4, copy _11); + StorageDead(_25); + StorageDead(_24); + _22 = opaque::(move _23) -> [return: bb4, unwind continue]; } bb4: { - StorageDead(_25); - StorageDead(_24); + StorageDead(_23); + StorageDead(_22); + StorageLive(_27); + StorageLive(_28); StorageLive(_29); +- _29 = copy _3; ++ _29 = copy _4; StorageLive(_30); StorageLive(_31); -- _31 = copy _3; -+ _31 = copy _4; - StorageLive(_32); - StorageLive(_33); -- _33 = copy _11; -- _32 = move _33 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _33 = copy _12; -+ _32 = copy _12; - StorageDead(_33); -- _30 = Lt(move _31, move _32); -+ _30 = Lt(copy _4, copy _12); - StorageDead(_32); +- _31 = copy _10; +- _30 = move _31 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _31 = copy _11; ++ _30 = copy _11; StorageDead(_31); - _29 = opaque::(move _30) -> [return: bb5, unwind continue]; +- _28 = Lt(move _29, move _30); ++ _28 = Lt(copy _4, copy _11); + StorageDead(_30); + StorageDead(_29); + _27 = opaque::(move _28) -> [return: bb5, unwind continue]; } bb5: { - StorageDead(_30); - StorageDead(_29); + StorageDead(_28); + StorageDead(_27); + StorageLive(_32); + StorageLive(_33); StorageLive(_34); +- _34 = copy _3; ++ _34 = copy _4; StorageLive(_35); StorageLive(_36); -- _36 = copy _3; -+ _36 = copy _4; - StorageLive(_37); - StorageLive(_38); -- _38 = copy _11; -- _37 = move _38 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _38 = copy _12; -+ _37 = copy _12; - StorageDead(_38); -- _35 = Le(move _36, move _37); -+ _35 = Le(copy _4, copy _12); - StorageDead(_37); +- _36 = copy _10; +- _35 = move _36 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _36 = copy _11; ++ _35 = copy _11; StorageDead(_36); - _34 = opaque::(move _35) -> [return: bb6, unwind continue]; +- _33 = Le(move _34, move _35); ++ _33 = Le(copy _4, copy _11); + StorageDead(_35); + StorageDead(_34); + _32 = opaque::(move _33) -> [return: bb6, unwind continue]; } bb6: { - StorageDead(_35); - StorageDead(_34); + StorageDead(_33); + StorageDead(_32); + StorageLive(_37); + StorageLive(_38); StorageLive(_39); +- _39 = copy _3; ++ _39 = copy _4; StorageLive(_40); StorageLive(_41); -- _41 = copy _3; -+ _41 = copy _4; - StorageLive(_42); - StorageLive(_43); -- _43 = copy _11; -- _42 = move _43 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _43 = copy _12; -+ _42 = copy _12; - StorageDead(_43); -- _40 = Gt(move _41, move _42); -+ _40 = Gt(copy _4, copy _12); - StorageDead(_42); +- _41 = copy _10; +- _40 = move _41 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _41 = copy _11; ++ _40 = copy _11; StorageDead(_41); - _39 = opaque::(move _40) -> [return: bb7, unwind continue]; +- _38 = Gt(move _39, move _40); ++ _38 = Gt(copy _4, copy _11); + StorageDead(_40); + StorageDead(_39); + _37 = opaque::(move _38) -> [return: bb7, unwind continue]; } bb7: { - StorageDead(_40); - StorageDead(_39); + StorageDead(_38); + StorageDead(_37); + StorageLive(_42); + StorageLive(_43); StorageLive(_44); +- _44 = copy _3; ++ _44 = copy _4; StorageLive(_45); StorageLive(_46); -- _46 = copy _3; -+ _46 = copy _4; - StorageLive(_47); - StorageLive(_48); -- _48 = copy _11; -- _47 = move _48 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); -+ _48 = copy _12; -+ _47 = copy _12; - StorageDead(_48); -- _45 = Ge(move _46, move _47); -+ _45 = Ge(copy _4, copy _12); - StorageDead(_47); +- _46 = copy _10; +- _45 = move _46 as *const dyn std::marker::Send (PointerCoercion(Unsize, Implicit)); ++ _46 = copy _11; ++ _45 = copy _11; StorageDead(_46); - _44 = opaque::(move _45) -> [return: bb8, unwind continue]; +- _43 = Ge(move _44, move _45); ++ _43 = Ge(copy _4, copy _11); + StorageDead(_45); + StorageDead(_44); + _42 = opaque::(move _43) -> [return: bb8, unwind continue]; } bb8: { - StorageDead(_45); - StorageDead(_44); + StorageDead(_43); + StorageDead(_42); _0 = const (); - StorageDead(_16); - StorageDead(_11); + StorageDead(_15); + StorageDead(_10); StorageDead(_8); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff deleted file mode 100644 index f39df7ffca0f..000000000000 --- a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `norm2` before InstSimplify-after-simplifycfg -+ // MIR for `norm2` after InstSimplify-after-simplifycfg - - fn norm2(_1: [f32; 2]) -> f32 { - debug x => _1; - let mut _0: f32; - let _2: f32; - let _3: usize; - let mut _4: usize; - let mut _5: bool; - let _7: usize; - let mut _8: usize; - let mut _9: bool; - let mut _10: f32; - let mut _11: f32; - let mut _12: f32; - let mut _13: f32; - let mut _14: f32; - let mut _15: f32; - scope 1 { - debug a => _2; - let _6: f32; - scope 2 { - debug b => _6; - } - } - - bb0: { - StorageLive(_2); - StorageLive(_3); - _3 = const 0_usize; -- _4 = Len(_1); -+ _4 = const 2_usize; - _5 = Lt(copy _3, copy _4); - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind unreachable]; - } - - bb1: { - _2 = copy _1[_3]; - StorageDead(_3); - StorageLive(_6); - StorageLive(_7); - _7 = const 1_usize; -- _8 = Len(_1); -+ _8 = const 2_usize; - _9 = Lt(copy _7, copy _8); - assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind unreachable]; - } - - bb2: { - _6 = copy _1[_7]; - StorageDead(_7); - StorageLive(_10); - StorageLive(_11); - _11 = copy _2; - StorageLive(_12); - _12 = copy _2; - _10 = Mul(move _11, move _12); - StorageDead(_12); - StorageDead(_11); - StorageLive(_13); - StorageLive(_14); - _14 = copy _6; - StorageLive(_15); - _15 = copy _6; - _13 = Mul(move _14, move _15); - StorageDead(_15); - StorageDead(_14); - _0 = Add(move _10, move _13); - StorageDead(_13); - StorageDead(_10); - StorageDead(_6); - StorageDead(_2); - return; - } - } - diff --git a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff deleted file mode 100644 index 0e7d5653c682..000000000000 --- a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff +++ /dev/null @@ -1,77 +0,0 @@ -- // MIR for `norm2` before InstSimplify-after-simplifycfg -+ // MIR for `norm2` after InstSimplify-after-simplifycfg - - fn norm2(_1: [f32; 2]) -> f32 { - debug x => _1; - let mut _0: f32; - let _2: f32; - let _3: usize; - let mut _4: usize; - let mut _5: bool; - let _7: usize; - let mut _8: usize; - let mut _9: bool; - let mut _10: f32; - let mut _11: f32; - let mut _12: f32; - let mut _13: f32; - let mut _14: f32; - let mut _15: f32; - scope 1 { - debug a => _2; - let _6: f32; - scope 2 { - debug b => _6; - } - } - - bb0: { - StorageLive(_2); - StorageLive(_3); - _3 = const 0_usize; -- _4 = Len(_1); -+ _4 = const 2_usize; - _5 = Lt(copy _3, copy _4); - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind continue]; - } - - bb1: { - _2 = copy _1[_3]; - StorageDead(_3); - StorageLive(_6); - StorageLive(_7); - _7 = const 1_usize; -- _8 = Len(_1); -+ _8 = const 2_usize; - _9 = Lt(copy _7, copy _8); - assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb2, unwind continue]; - } - - bb2: { - _6 = copy _1[_7]; - StorageDead(_7); - StorageLive(_10); - StorageLive(_11); - _11 = copy _2; - StorageLive(_12); - _12 = copy _2; - _10 = Mul(move _11, move _12); - StorageDead(_12); - StorageDead(_11); - StorageLive(_13); - StorageLive(_14); - _14 = copy _6; - StorageLive(_15); - _15 = copy _6; - _13 = Mul(move _14, move _15); - StorageDead(_15); - StorageDead(_14); - _0 = Add(move _10, move _13); - StorageDead(_13); - StorageDead(_10); - StorageDead(_6); - StorageDead(_2); - return; - } - } - diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs deleted file mode 100644 index 91f43f75689d..000000000000 --- a/tests/mir-opt/instsimplify/combine_array_len.rs +++ /dev/null @@ -1,15 +0,0 @@ -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ test-mir-pass: InstSimplify-after-simplifycfg - -// EMIT_MIR combine_array_len.norm2.InstSimplify-after-simplifycfg.diff -fn norm2(x: [f32; 2]) -> f32 { - // CHECK-LABEL: fn norm2( - // CHECK-NOT: Len( - let a = x[0]; - let b = x[1]; - a * a + b * b -} - -fn main() { - assert_eq!(norm2([3.0, 4.0]), 5.0 * 5.0); -} diff --git a/tests/mir-opt/issue_72181.foo.built.after.mir b/tests/mir-opt/issue_72181.foo.built.after.mir index 314cf8b367f5..7593b7954325 100644 --- a/tests/mir-opt/issue_72181.foo.built.after.mir +++ b/tests/mir-opt/issue_72181.foo.built.after.mir @@ -4,15 +4,14 @@ fn foo(_1: [(Never, u32); 1]) -> u32 { debug xs => _1; let mut _0: u32; let _2: usize; - let mut _3: usize; - let mut _4: bool; + let mut _3: bool; bb0: { StorageLive(_2); _2 = const 0_usize; - _3 = Len(_1); - _4 = Lt(copy _2, copy _3); - assert(move _4, "index out of bounds: the length is {} but the index is {}", move _3, copy _2) -> [success: bb1, unwind: bb2]; + FakeRead(ForIndex, _1); + _3 = Lt(copy _2, const 1_usize); + assert(move _3, "index out of bounds: the length is {} but the index is {}", const 1_usize, copy _2) -> [success: bb1, unwind: bb2]; } bb1: { diff --git a/tests/mir-opt/issue_72181.main.built.after.mir b/tests/mir-opt/issue_72181.main.built.after.mir index aade84a6dd2e..9f3803f5407f 100644 --- a/tests/mir-opt/issue_72181.main.built.after.mir +++ b/tests/mir-opt/issue_72181.main.built.after.mir @@ -7,8 +7,7 @@ fn main() -> () { let mut _4: Foo; let mut _5: u64; let _6: usize; - let mut _7: usize; - let mut _8: bool; + let mut _7: bool; scope 1 { let _2: [Foo; 2]; scope 2 { @@ -38,9 +37,9 @@ fn main() -> () { StorageLive(_5); StorageLive(_6); _6 = const 0_usize; - _7 = Len(_2); - _8 = Lt(copy _6, copy _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb3, unwind: bb5]; + FakeRead(ForIndex, _2); + _7 = Lt(copy _6, const 2_usize); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _6) -> [success: bb3, unwind: bb5]; } bb2: { diff --git a/tests/mir-opt/issue_91633.foo.built.after.mir b/tests/mir-opt/issue_91633.foo.built.after.mir index 50fdf08375a0..bf65b5b4a8cc 100644 --- a/tests/mir-opt/issue_91633.foo.built.after.mir +++ b/tests/mir-opt/issue_91633.foo.built.after.mir @@ -6,8 +6,9 @@ fn foo(_1: Box<[T]>) -> T { let _2: T; let mut _3: &T; let _4: usize; - let mut _5: usize; - let mut _6: bool; + let mut _5: *const [T]; + let mut _6: usize; + let mut _7: bool; scope 1 { debug f => _2; } @@ -17,9 +18,10 @@ fn foo(_1: Box<[T]>) -> T { StorageLive(_3); StorageLive(_4); _4 = const 0_usize; - _5 = Len((*_1)); - _6 = Lt(copy _4, copy _5); - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, copy _4) -> [success: bb1, unwind: bb5]; + _5 = &raw const (*_1); + _6 = PtrMetadata(move _5); + _7 = Lt(copy _4, copy _6); + assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb5]; } bb1: { diff --git a/tests/mir-opt/issue_91633.fun.built.after.mir b/tests/mir-opt/issue_91633.fun.built.after.mir index 5b41b376719b..d2fc438d3e81 100644 --- a/tests/mir-opt/issue_91633.fun.built.after.mir +++ b/tests/mir-opt/issue_91633.fun.built.after.mir @@ -15,7 +15,7 @@ fn fun(_1: &[T]) -> &T { StorageLive(_2); StorageLive(_3); _3 = const 0_usize; - _4 = Len((*_1)); + _4 = PtrMetadata(copy _1); _5 = Lt(copy _3, copy _4); assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind: bb2]; } diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff index f052c8f63dca..98c5e868046b 100644 --- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-abort.diff @@ -11,16 +11,14 @@ let mut _6: &[u8]; let mut _7: &[u8; N]; let _8: usize; - let mut _9: usize; - let mut _10: bool; + let mut _9: bool; bb0: { - StorageLive(_3); + nop; StorageLive(_4); _4 = copy _1; -- StorageLive(_5); -+ nop; + StorageLive(_5); StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -40,16 +38,13 @@ } bb2: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); StorageLive(_8); _8 = copy _1; -- _9 = Len((*_2)); -- _10 = Lt(copy _8, copy _9); -- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb3, unwind unreachable]; -+ _9 = const N; -+ _10 = copy _3; +- _9 = Lt(copy _8, const N); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind unreachable]; ++ _9 = copy _3; + assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind unreachable]; } @@ -61,8 +56,7 @@ } bb4: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); _0 = const 42_u8; goto -> bb5; diff --git a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff index 3299e3004317..72c731378699 100644 --- a/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound.GVN.panic-unwind.diff @@ -11,16 +11,14 @@ let mut _6: &[u8]; let mut _7: &[u8; N]; let _8: usize; - let mut _9: usize; - let mut _10: bool; + let mut _9: bool; bb0: { - StorageLive(_3); + nop; StorageLive(_4); _4 = copy _1; -- StorageLive(_5); -+ nop; + StorageLive(_5); StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -40,16 +38,13 @@ } bb2: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); StorageLive(_8); _8 = copy _1; -- _9 = Len((*_2)); -- _10 = Lt(copy _8, copy _9); -- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb3, unwind continue]; -+ _9 = const N; -+ _10 = copy _3; +- _9 = Lt(copy _8, const N); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind continue]; ++ _9 = copy _3; + assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind continue]; } @@ -61,8 +56,7 @@ } bb4: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); _0 = const 42_u8; goto -> bb5; diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff index 329eb80b3c4f..9ffaf44c02bd 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-abort.diff @@ -11,19 +11,16 @@ let mut _6: &[u8]; let mut _7: &[u8; N]; let _8: usize; - let mut _9: usize; - let mut _10: bool; - let _11: usize; - let mut _12: usize; - let mut _13: bool; + let mut _9: bool; + let _10: usize; + let mut _11: bool; bb0: { - StorageLive(_3); + nop; StorageLive(_4); _4 = copy _1; -- StorageLive(_5); -+ nop; + StorageLive(_5); StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -43,16 +40,13 @@ } bb2: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); StorageLive(_8); _8 = copy _1; -- _9 = Len((*_2)); -- _10 = Lt(copy _8, copy _9); -- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb3, unwind unreachable]; -+ _9 = const N; -+ _10 = copy _3; +- _9 = Lt(copy _8, const N); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind unreachable]; ++ _9 = copy _3; + assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind unreachable]; } @@ -64,23 +58,20 @@ } bb4: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); - StorageLive(_11); - _11 = const 0_usize; -- _12 = Len((*_2)); -- _13 = Lt(copy _11, copy _12); -- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb5, unwind unreachable]; -+ _12 = const N; -+ _13 = Lt(const 0_usize, const N); -+ assert(move _13, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb5, unwind unreachable]; + StorageLive(_10); + _10 = const 0_usize; +- _11 = Lt(copy _10, const N); +- assert(move _11, "index out of bounds: the length is {} but the index is {}", const N, copy _10) -> [success: bb5, unwind unreachable]; ++ _11 = Lt(const 0_usize, const N); ++ assert(move _11, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb5, unwind unreachable]; } bb5: { -- (*_2)[_11] = const 42_u8; +- (*_2)[_10] = const 42_u8; + (*_2)[0 of 1] = const 42_u8; - StorageDead(_11); + StorageDead(_10); _0 = const 42_u8; goto -> bb6; } diff --git a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff index ab007e133ecc..08008e463357 100644 --- a/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_bound_mut.GVN.panic-unwind.diff @@ -11,19 +11,16 @@ let mut _6: &[u8]; let mut _7: &[u8; N]; let _8: usize; - let mut _9: usize; - let mut _10: bool; - let _11: usize; - let mut _12: usize; - let mut _13: bool; + let mut _9: bool; + let _10: usize; + let mut _11: bool; bb0: { - StorageLive(_3); + nop; StorageLive(_4); _4 = copy _1; -- StorageLive(_5); -+ nop; + StorageLive(_5); StorageLive(_6); StorageLive(_7); _7 = &(*_2); @@ -43,16 +40,13 @@ } bb2: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); StorageLive(_8); _8 = copy _1; -- _9 = Len((*_2)); -- _10 = Lt(copy _8, copy _9); -- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, copy _8) -> [success: bb3, unwind continue]; -+ _9 = const N; -+ _10 = copy _3; +- _9 = Lt(copy _8, const N); +- assert(move _9, "index out of bounds: the length is {} but the index is {}", const N, copy _8) -> [success: bb3, unwind continue]; ++ _9 = copy _3; + assert(copy _3, "index out of bounds: the length is {} but the index is {}", const N, copy _1) -> [success: bb3, unwind continue]; } @@ -64,23 +58,20 @@ } bb4: { -- StorageDead(_5); -+ nop; + StorageDead(_5); StorageDead(_4); - StorageLive(_11); - _11 = const 0_usize; -- _12 = Len((*_2)); -- _13 = Lt(copy _11, copy _12); -- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb5, unwind continue]; -+ _12 = const N; -+ _13 = Lt(const 0_usize, const N); -+ assert(move _13, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb5, unwind continue]; + StorageLive(_10); + _10 = const 0_usize; +- _11 = Lt(copy _10, const N); +- assert(move _11, "index out of bounds: the length is {} but the index is {}", const N, copy _10) -> [success: bb5, unwind continue]; ++ _11 = Lt(const 0_usize, const N); ++ assert(move _11, "index out of bounds: the length is {} but the index is {}", const N, const 0_usize) -> [success: bb5, unwind continue]; } bb5: { -- (*_2)[_11] = const 42_u8; +- (*_2)[_10] = const 42_u8; + (*_2)[0 of 1] = const 42_u8; - StorageDead(_11); + StorageDead(_10); _0 = const 42_u8; goto -> bb6; } diff --git a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff index 20001f1248ef..4b39e18d16cf 100644 --- a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff +++ b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-abort.diff @@ -36,7 +36,7 @@ StorageDead(_4); StorageLive(_7); _7 = copy _1; - _8 = Len((*_2)); + _8 = PtrMetadata(copy _2); _9 = Lt(copy _7, copy _8); assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff index ca8f92df5de0..f0d4afa21ae4 100644 --- a/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff +++ b/tests/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.panic-unwind.diff @@ -36,7 +36,7 @@ StorageDead(_4); StorageLive(_7); _7 = copy _1; - _8 = Len((*_2)); + _8 = PtrMetadata(copy _2); _9 = Lt(copy _7, copy _8); assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, copy _7) -> [success: bb3, unwind continue]; } diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir index 7294302609a2..42b388033360 100644 --- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir +++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir @@ -27,20 +27,19 @@ fn main() -> () { let mut _0: (); let mut _1: [usize; ValTree(Leaf(0x00000003): usize)]; let _3: usize; - let mut _4: usize; - let mut _5: bool; - let mut _7: bool; - let _8: bool; - let mut _9: usize; - let _10: bool; + let mut _4: bool; + let mut _6: bool; + let _7: bool; + let mut _8: usize; + let _9: bool; scope 1 { debug v => _1; let _2: &'?3 usize; scope 2 { debug p => _2; - let _6: &'?4 usize; + let _5: &'?4 usize; scope 3 { - debug q => _6; + debug q => _5; } } } @@ -52,50 +51,50 @@ fn main() -> () { StorageLive(_2); StorageLive(_3); _3 = const ConstValue(Scalar(0x00000000): usize); - _4 = Len(_1); - _5 = Lt(copy _3, copy _4); - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind: bb7]; + FakeRead(ForIndex, _1); + _4 = Lt(copy _3, const ValTree(Leaf(0x00000003): usize)); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const ValTree(Leaf(0x00000003): usize), copy _3) -> [success: bb1, unwind: bb7]; } bb1: { _2 = &'?2 _1[_3]; FakeRead(ForLet(None), _2); + StorageLive(_5); + _5 = copy _2; + FakeRead(ForLet(None), _5); StorageLive(_6); - _6 = copy _2; - FakeRead(ForLet(None), _6); - StorageLive(_7); - _7 = const ConstValue(Scalar(0x01): bool); - switchInt(move _7) -> [0: bb4, otherwise: bb2]; + _6 = const ConstValue(Scalar(0x01): bool); + switchInt(move _6) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_7); StorageLive(_8); - StorageLive(_9); - _9 = copy (*_6); - _8 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(move _9) -> [return: bb3, unwind: bb7]; + _8 = copy (*_5); + _7 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(move _8) -> [return: bb3, unwind: bb7]; } bb3: { - StorageDead(_9); StorageDead(_8); + StorageDead(_7); _0 = const ConstValue(ZeroSized: ()); goto -> bb6; } bb4: { - StorageLive(_10); - _10 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(const ConstValue(Scalar(0x00000016): usize)) -> [return: bb5, unwind: bb7]; + StorageLive(_9); + _9 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(const ConstValue(Scalar(0x00000016): usize)) -> [return: bb5, unwind: bb7]; } bb5: { - StorageDead(_10); + StorageDead(_9); _0 = const ConstValue(ZeroSized: ()); goto -> bb6; } bb6: { - StorageDead(_7); StorageDead(_6); + StorageDead(_5); StorageDead(_3); StorageDead(_2); StorageDead(_1); diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir index 85b89a013c4e..15395fd470e9 100644 --- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir +++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir @@ -27,20 +27,19 @@ fn main() -> () { let mut _0: (); let mut _1: [usize; ValTree(Leaf(0x0000000000000003): usize)]; let _3: usize; - let mut _4: usize; - let mut _5: bool; - let mut _7: bool; - let _8: bool; - let mut _9: usize; - let _10: bool; + let mut _4: bool; + let mut _6: bool; + let _7: bool; + let mut _8: usize; + let _9: bool; scope 1 { debug v => _1; let _2: &'?3 usize; scope 2 { debug p => _2; - let _6: &'?4 usize; + let _5: &'?4 usize; scope 3 { - debug q => _6; + debug q => _5; } } } @@ -52,50 +51,50 @@ fn main() -> () { StorageLive(_2); StorageLive(_3); _3 = const ConstValue(Scalar(0x0000000000000000): usize); - _4 = Len(_1); - _5 = Lt(copy _3, copy _4); - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, copy _3) -> [success: bb1, unwind: bb7]; + FakeRead(ForIndex, _1); + _4 = Lt(copy _3, const ValTree(Leaf(0x0000000000000003): usize)); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const ValTree(Leaf(0x0000000000000003): usize), copy _3) -> [success: bb1, unwind: bb7]; } bb1: { _2 = &'?2 _1[_3]; FakeRead(ForLet(None), _2); + StorageLive(_5); + _5 = copy _2; + FakeRead(ForLet(None), _5); StorageLive(_6); - _6 = copy _2; - FakeRead(ForLet(None), _6); - StorageLive(_7); - _7 = const ConstValue(Scalar(0x01): bool); - switchInt(move _7) -> [0: bb4, otherwise: bb2]; + _6 = const ConstValue(Scalar(0x01): bool); + switchInt(move _6) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_7); StorageLive(_8); - StorageLive(_9); - _9 = copy (*_6); - _8 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(move _9) -> [return: bb3, unwind: bb7]; + _8 = copy (*_5); + _7 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(move _8) -> [return: bb3, unwind: bb7]; } bb3: { - StorageDead(_9); StorageDead(_8); + StorageDead(_7); _0 = const ConstValue(ZeroSized: ()); goto -> bb6; } bb4: { - StorageLive(_10); - _10 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(const ConstValue(Scalar(0x0000000000000016): usize)) -> [return: bb5, unwind: bb7]; + StorageLive(_9); + _9 = ConstValue(ZeroSized: fn(usize) -> bool {use_x})(const ConstValue(Scalar(0x0000000000000016): usize)) -> [return: bb5, unwind: bb7]; } bb5: { - StorageDead(_10); + StorageDead(_9); _0 = const ConstValue(ZeroSized: ()); goto -> bb6; } bb6: { - StorageDead(_7); StorageDead(_6); + StorageDead(_5); StorageDead(_3); StorageDead(_2); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff index 6575610727b9..5b39e45806e3 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-abort.diff @@ -7,17 +7,16 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: u32; + let mut _6: bool; + let mut _8: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -38,10 +37,9 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; -- _7 = Lt(copy _5, copy _6); -- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind unreachable]; -+ _7 = const true; +- _6 = Lt(copy _5, const 6_usize); +- assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind unreachable]; ++ _6 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 6_usize, const 3_usize) -> [success: bb2, unwind unreachable]; } @@ -50,13 +48,13 @@ + _3 = const 3_i32; StorageDead(_5); StorageDead(_4); + StorageLive(_7); StorageLive(_8); - StorageLive(_9); - _9 = const 42_u32; -- _8 = copy _9; -+ _8 = const 42_u32; - StorageDead(_9); + _8 = const 42_u32; +- _7 = copy _8; ++ _7 = const 42_u32; StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff index 1a4ed5767fec..ea2742a6471a 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.32bit.panic-unwind.diff @@ -7,17 +7,16 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: u32; + let mut _6: bool; + let mut _8: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -38,10 +37,9 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; -- _7 = Lt(copy _5, copy _6); -- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind continue]; -+ _7 = const true; +- _6 = Lt(copy _5, const 6_usize); +- assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind continue]; ++ _6 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 6_usize, const 3_usize) -> [success: bb2, unwind continue]; } @@ -50,13 +48,13 @@ + _3 = const 3_i32; StorageDead(_5); StorageDead(_4); + StorageLive(_7); StorageLive(_8); - StorageLive(_9); - _9 = const 42_u32; -- _8 = copy _9; -+ _8 = const 42_u32; - StorageDead(_9); + _8 = const 42_u32; +- _7 = copy _8; ++ _7 = const 42_u32; StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff index 6575610727b9..5b39e45806e3 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-abort.diff @@ -7,17 +7,16 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: u32; + let mut _6: bool; + let mut _8: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -38,10 +37,9 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; -- _7 = Lt(copy _5, copy _6); -- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind unreachable]; -+ _7 = const true; +- _6 = Lt(copy _5, const 6_usize); +- assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind unreachable]; ++ _6 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 6_usize, const 3_usize) -> [success: bb2, unwind unreachable]; } @@ -50,13 +48,13 @@ + _3 = const 3_i32; StorageDead(_5); StorageDead(_4); + StorageLive(_7); StorageLive(_8); - StorageLive(_9); - _9 = const 42_u32; -- _8 = copy _9; -+ _8 = const 42_u32; - StorageDead(_9); + _8 = const 42_u32; +- _7 = copy _8; ++ _7 = const 42_u32; StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff index 1a4ed5767fec..ea2742a6471a 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.GVN.64bit.panic-unwind.diff @@ -7,17 +7,16 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: u32; + let mut _6: bool; + let mut _8: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -38,10 +37,9 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; -- _7 = Lt(copy _5, copy _6); -- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind continue]; -+ _7 = const true; +- _6 = Lt(copy _5, const 6_usize); +- assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind continue]; ++ _6 = const true; + assert(const true, "index out of bounds: the length is {} but the index is {}", const 6_usize, const 3_usize) -> [success: bb2, unwind continue]; } @@ -50,13 +48,13 @@ + _3 = const 3_i32; StorageDead(_5); StorageDead(_4); + StorageLive(_7); StorageLive(_8); - StorageLive(_9); - _9 = const 42_u32; -- _8 = copy _9; -+ _8 = const 42_u32; - StorageDead(_9); + _8 = const 42_u32; +- _7 = copy _8; ++ _7 = const 42_u32; StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff index e2420a341e0a..f7fe08831b97 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff @@ -7,19 +7,18 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: Point; + let mut _6: bool; + let mut _8: Point; ++ let mut _9: u32; + let mut _10: u32; -+ let mut _11: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -37,31 +36,30 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; - _7 = Lt(copy _5, copy _6); - assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind unreachable]; + _6 = Lt(copy _5, const 6_usize); + assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind unreachable]; } bb2: { _3 = copy _4[_5]; StorageDead(_5); StorageDead(_4); - StorageLive(_8); -- StorageLive(_9); -- _9 = Point { x: const 12_u32, y: const 42_u32 }; -- _8 = copy (_9.1: u32); -- StorageDead(_9); + StorageLive(_7); +- StorageLive(_8); +- _8 = Point { x: const 12_u32, y: const 42_u32 }; +- _7 = copy (_8.1: u32); +- StorageDead(_8); ++ StorageLive(_9); + StorageLive(_10); -+ StorageLive(_11); + nop; -+ _10 = const 12_u32; -+ _11 = const 42_u32; ++ _9 = const 12_u32; ++ _10 = const 42_u32; + nop; -+ _8 = copy _11; ++ _7 = copy _10; ++ StorageDead(_9); + StorageDead(_10); -+ StorageDead(_11); + nop; - StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff index a2fb3b979e62..6e36386bea60 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff @@ -7,19 +7,18 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: Point; + let mut _6: bool; + let mut _8: Point; ++ let mut _9: u32; + let mut _10: u32; -+ let mut _11: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -37,31 +36,30 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; - _7 = Lt(copy _5, copy _6); - assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind continue]; + _6 = Lt(copy _5, const 6_usize); + assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind continue]; } bb2: { _3 = copy _4[_5]; StorageDead(_5); StorageDead(_4); - StorageLive(_8); -- StorageLive(_9); -- _9 = Point { x: const 12_u32, y: const 42_u32 }; -- _8 = copy (_9.1: u32); -- StorageDead(_9); + StorageLive(_7); +- StorageLive(_8); +- _8 = Point { x: const 12_u32, y: const 42_u32 }; +- _7 = copy (_8.1: u32); +- StorageDead(_8); ++ StorageLive(_9); + StorageLive(_10); -+ StorageLive(_11); + nop; -+ _10 = const 12_u32; -+ _11 = const 42_u32; ++ _9 = const 12_u32; ++ _10 = const 42_u32; + nop; -+ _8 = copy _11; ++ _7 = copy _10; ++ StorageDead(_9); + StorageDead(_10); -+ StorageDead(_11); + nop; - StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff index e2420a341e0a..f7fe08831b97 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff @@ -7,19 +7,18 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: Point; + let mut _6: bool; + let mut _8: Point; ++ let mut _9: u32; + let mut _10: u32; -+ let mut _11: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -37,31 +36,30 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; - _7 = Lt(copy _5, copy _6); - assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind unreachable]; + _6 = Lt(copy _5, const 6_usize); + assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind unreachable]; } bb2: { _3 = copy _4[_5]; StorageDead(_5); StorageDead(_4); - StorageLive(_8); -- StorageLive(_9); -- _9 = Point { x: const 12_u32, y: const 42_u32 }; -- _8 = copy (_9.1: u32); -- StorageDead(_9); + StorageLive(_7); +- StorageLive(_8); +- _8 = Point { x: const 12_u32, y: const 42_u32 }; +- _7 = copy (_8.1: u32); +- StorageDead(_8); ++ StorageLive(_9); + StorageLive(_10); -+ StorageLive(_11); + nop; -+ _10 = const 12_u32; -+ _11 = const 42_u32; ++ _9 = const 12_u32; ++ _10 = const 42_u32; + nop; -+ _8 = copy _11; ++ _7 = copy _10; ++ StorageDead(_9); + StorageDead(_10); -+ StorageDead(_11); + nop; - StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff index a2fb3b979e62..6e36386bea60 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff @@ -7,19 +7,18 @@ let mut _2: (i32, bool); let mut _4: [i32; 6]; let _5: usize; - let mut _6: usize; - let mut _7: bool; - let mut _9: Point; + let mut _6: bool; + let mut _8: Point; ++ let mut _9: u32; + let mut _10: u32; -+ let mut _11: u32; scope 1 { debug x => _1; let _3: i32; scope 2 { debug y => _3; - let _8: u32; + let _7: u32; scope 3 { - debug z => _8; + debug z => _7; } } } @@ -37,31 +36,30 @@ _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; StorageLive(_5); _5 = const 3_usize; - _6 = const 6_usize; - _7 = Lt(copy _5, copy _6); - assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _5) -> [success: bb2, unwind continue]; + _6 = Lt(copy _5, const 6_usize); + assert(move _6, "index out of bounds: the length is {} but the index is {}", const 6_usize, copy _5) -> [success: bb2, unwind continue]; } bb2: { _3 = copy _4[_5]; StorageDead(_5); StorageDead(_4); - StorageLive(_8); -- StorageLive(_9); -- _9 = Point { x: const 12_u32, y: const 42_u32 }; -- _8 = copy (_9.1: u32); -- StorageDead(_9); + StorageLive(_7); +- StorageLive(_8); +- _8 = Point { x: const 12_u32, y: const 42_u32 }; +- _7 = copy (_8.1: u32); +- StorageDead(_8); ++ StorageLive(_9); + StorageLive(_10); -+ StorageLive(_11); + nop; -+ _10 = const 12_u32; -+ _11 = const 42_u32; ++ _9 = const 12_u32; ++ _10 = const 42_u32; + nop; -+ _8 = copy _11; ++ _7 = copy _10; ++ StorageDead(_9); + StorageDead(_10); -+ StorageDead(_11); + nop; - StorageDead(_8); + StorageDead(_7); StorageDead(_3); StorageDead(_1); return; diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs index 574062d6c356..5dac535d195a 100644 --- a/tests/mir-opt/pre-codegen/slice_index.rs +++ b/tests/mir-opt/pre-codegen/slice_index.rs @@ -9,7 +9,7 @@ use std::ops::Range; // EMIT_MIR slice_index.slice_index_usize.PreCodegen.after.mir pub fn slice_index_usize(slice: &[u32], index: usize) -> u32 { // CHECK-LABEL: slice_index_usize - // CHECK: [[LEN:_[0-9]+]] = Len((*_1)) + // CHECK: [[LEN:_[0-9]+]] = PtrMetadata(copy _1) // CHECK: Lt(copy _2, copy [[LEN]]) // CHECK-NOT: precondition_check // CHECK: _0 = copy (*_1)[_2]; diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-abort.mir index cc1034229fc3..81e60b8ec2ca 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-abort.mir @@ -8,7 +8,7 @@ fn slice_index_usize(_1: &[u32], _2: usize) -> u32 { let mut _4: bool; bb0: { - _3 = Len((*_1)); + _3 = PtrMetadata(copy _1); _4 = Lt(copy _2, copy _3); assert(move _4, "index out of bounds: the length is {} but the index is {}", move _3, copy _2) -> [success: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-unwind.mir index 358226fb5294..c0fdc839608d 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_usize.PreCodegen.after.panic-unwind.mir @@ -8,7 +8,7 @@ fn slice_index_usize(_1: &[u32], _2: usize) -> u32 { let mut _4: bool; bb0: { - _3 = Len((*_1)); + _3 = PtrMetadata(copy _1); _4 = Lt(copy _2, copy _3); assert(move _4, "index out of bounds: the length is {} but the index is {}", move _3, copy _2) -> [success: bb1, unwind continue]; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir index ecac03ad0f9d..151783969dd7 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir @@ -7,20 +7,19 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _3: usize; let mut _4: usize; let mut _9: std::option::Option; - let mut _11: usize; - let mut _12: bool; - let mut _14: &impl Fn(usize, &T); - let mut _15: (usize, &T); - let _16: (); + let mut _11: bool; + let mut _13: &impl Fn(usize, &T); + let mut _14: (usize, &T); + let _15: (); scope 1 { debug ((iter: std::ops::Range).0: usize) => _4; debug ((iter: std::ops::Range).1: usize) => _3; let _10: usize; scope 2 { debug i => _10; - let _13: &T; + let _12: &T; scope 3 { - debug x => _13; + debug x => _12; } } scope 5 (inlined iter::range::>::next) { @@ -82,23 +81,22 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageDead(_6); StorageDead(_7); _10 = copy ((_9 as Some).0: usize); - _11 = Len((*_1)); - _12 = Lt(copy _10, copy _11); - assert(move _12, "index out of bounds: the length is {} but the index is {}", move _11, copy _10) -> [success: bb6, unwind unreachable]; + _11 = Lt(copy _10, copy _3); + assert(move _11, "index out of bounds: the length is {} but the index is {}", copy _3, copy _10) -> [success: bb6, unwind unreachable]; } bb6: { - _13 = &(*_1)[_10]; + _12 = &(*_1)[_10]; + StorageLive(_13); + _13 = &_2; StorageLive(_14); - _14 = &_2; - StorageLive(_15); - _15 = (copy _10, copy _13); - _16 = >::call(move _14, move _15) -> [return: bb7, unwind unreachable]; + _14 = (copy _10, copy _12); + _15 = >::call(move _13, move _14) -> [return: bb7, unwind unreachable]; } bb7: { - StorageDead(_15); StorageDead(_14); + StorageDead(_13); StorageDead(_9); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir index 1032473b9b2a..006329dc20dc 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir @@ -7,20 +7,19 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _3: usize; let mut _4: usize; let mut _9: std::option::Option; - let mut _11: usize; - let mut _12: bool; - let mut _14: &impl Fn(usize, &T); - let mut _15: (usize, &T); - let _16: (); + let mut _11: bool; + let mut _13: &impl Fn(usize, &T); + let mut _14: (usize, &T); + let _15: (); scope 1 { debug ((iter: std::ops::Range).0: usize) => _4; debug ((iter: std::ops::Range).1: usize) => _3; let _10: usize; scope 2 { debug i => _10; - let _13: &T; + let _12: &T; scope 3 { - debug x => _13; + debug x => _12; } } scope 5 (inlined iter::range::>::next) { @@ -82,23 +81,22 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageDead(_6); StorageDead(_7); _10 = copy ((_9 as Some).0: usize); - _11 = Len((*_1)); - _12 = Lt(copy _10, copy _11); - assert(move _12, "index out of bounds: the length is {} but the index is {}", move _11, copy _10) -> [success: bb6, unwind: bb8]; + _11 = Lt(copy _10, copy _3); + assert(move _11, "index out of bounds: the length is {} but the index is {}", copy _3, copy _10) -> [success: bb6, unwind: bb8]; } bb6: { - _13 = &(*_1)[_10]; + _12 = &(*_1)[_10]; + StorageLive(_13); + _13 = &_2; StorageLive(_14); - _14 = &_2; - StorageLive(_15); - _15 = (copy _10, copy _13); - _16 = >::call(move _14, move _15) -> [return: bb7, unwind: bb8]; + _14 = (copy _10, copy _12); + _15 = >::call(move _13, move _14) -> [return: bb7, unwind: bb8]; } bb7: { - StorageDead(_15); StorageDead(_14); + StorageDead(_13); StorageDead(_9); goto -> bb1; } diff --git a/tests/ui/borrowck/borrowck-describe-lvalue.rs b/tests/ui/borrowck/borrowck-describe-lvalue.rs index cdcff69d6e52..f3a4b382fa89 100644 --- a/tests/ui/borrowck/borrowck-describe-lvalue.rs +++ b/tests/ui/borrowck/borrowck-describe-lvalue.rs @@ -231,7 +231,6 @@ fn main() { let x = &mut v; v[0].y; //~^ ERROR cannot use `v[_].y` because it was mutably borrowed - //~| ERROR cannot use `*v` because it was mutably borrowed drop(x); } // Field of constant index diff --git a/tests/ui/borrowck/borrowck-describe-lvalue.stderr b/tests/ui/borrowck/borrowck-describe-lvalue.stderr index 11f2e42d42b7..666a21808d80 100644 --- a/tests/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/tests/ui/borrowck/borrowck-describe-lvalue.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:254:13 + --> $DIR/borrowck-describe-lvalue.rs:253:13 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -9,7 +9,7 @@ LL | *y = 1; | ------ first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:264:20 + --> $DIR/borrowck-describe-lvalue.rs:263:20 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -19,7 +19,7 @@ LL | *y = 1; | ------ first borrow later used here error: captured variable cannot escape `FnMut` closure body - --> $DIR/borrowck-describe-lvalue.rs:262:16 + --> $DIR/borrowck-describe-lvalue.rs:261:16 | LL | let mut x = 0; | ----- variable defined here @@ -300,17 +300,6 @@ LL | S { x: F { y: ref x0, .. }, .. } => LL | drop(x); | - mutable borrow later used here -error[E0503]: cannot use `*v` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:232:9 - | -LL | let x = &mut v; - | ------ `v` is borrowed here -LL | v[0].y; - | ^^^^ use of borrowed `v` -... -LL | drop(x); - | - borrow later used here - error[E0503]: cannot use `v[_].y` because it was mutably borrowed --> $DIR/borrowck-describe-lvalue.rs:232:9 | @@ -318,12 +307,12 @@ LL | let x = &mut v; | ------ `v` is borrowed here LL | v[0].y; | ^^^^^^ use of borrowed `v` -... +LL | LL | drop(x); | - borrow later used here error[E0502]: cannot borrow `v[..].x` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:243:24 + --> $DIR/borrowck-describe-lvalue.rs:242:24 | LL | let x = &mut v; | ------ mutable borrow occurs here @@ -357,7 +346,7 @@ LL | drop(x); | - mutable borrow later used here error[E0382]: use of moved value: `x` - --> $DIR/borrowck-describe-lvalue.rs:274:22 + --> $DIR/borrowck-describe-lvalue.rs:273:22 | LL | drop(x); | - value moved here @@ -366,7 +355,7 @@ LL | drop(x); | = note: move occurs because `x` has type `Vec`, which does not implement the `Copy` trait -error: aborting due to 32 previous errors +error: aborting due to 31 previous errors Some errors have detailed explanations: E0382, E0499, E0502, E0503. For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs index 3abc81e191eb..2d22c9a856f6 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs @@ -12,8 +12,7 @@ fn arrays_1() { // c will capture `arr` completely, therefore another index into the // array can't be modified here arr[1] += 10; - //~^ ERROR: cannot use `arr` because it was mutably borrowed - //~| ERROR: cannot use `arr[_]` because it was mutably borrowed + //~^ ERROR: cannot use `arr[_]` because it was mutably borrowed c(); } @@ -55,8 +54,7 @@ fn arrays_4() { // c will capture `arr` completely, therefore we cannot borrow another index // into the array. println!("{}", arr[3]); - //~^ ERROR: cannot use `arr` because it was mutably borrowed - //~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable + //~^ ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable c(); } diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr index 9e5200ef34b5..97ecdfab8205 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr @@ -1,17 +1,3 @@ -error[E0503]: cannot use `arr` because it was mutably borrowed - --> $DIR/arrays.rs:14:5 - | -LL | let mut c = || { - | -- `arr` is borrowed here -LL | arr[0] += 10; - | --- borrow occurs due to use of `arr` in closure -... -LL | arr[1] += 10; - | ^^^^^^ use of borrowed `arr` -... -LL | c(); - | - borrow later used here - error[E0503]: cannot use `arr[_]` because it was mutably borrowed --> $DIR/arrays.rs:14:5 | @@ -22,12 +8,12 @@ LL | arr[0] += 10; ... LL | arr[1] += 10; | ^^^^^^^^^^^^ use of borrowed `arr` -... +LL | LL | c(); | - borrow later used here error[E0506]: cannot assign to `arr[_]` because it is borrowed - --> $DIR/arrays.rs:29:5 + --> $DIR/arrays.rs:28:5 | LL | let c = || { | -- `arr[_]` is borrowed here @@ -41,7 +27,7 @@ LL | c(); | - borrow later used here error[E0506]: cannot assign to `arr[_]` because it is borrowed - --> $DIR/arrays.rs:43:5 + --> $DIR/arrays.rs:42:5 | LL | let c = || { | -- `arr[_]` is borrowed here @@ -54,22 +40,8 @@ LL | LL | c(); | - borrow later used here -error[E0503]: cannot use `arr` because it was mutably borrowed - --> $DIR/arrays.rs:57:20 - | -LL | let mut c = || { - | -- `arr` is borrowed here -LL | arr[1] += 10; - | --- borrow occurs due to use of `arr` in closure -... -LL | println!("{}", arr[3]); - | ^^^^^^ use of borrowed `arr` -... -LL | c(); - | - borrow later used here - error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable - --> $DIR/arrays.rs:57:20 + --> $DIR/arrays.rs:56:20 | LL | let mut c = || { | -- mutable borrow occurs here @@ -85,7 +57,7 @@ LL | c(); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable - --> $DIR/arrays.rs:73:24 + --> $DIR/arrays.rs:71:24 | LL | let mut c = || { | -- mutable borrow occurs here @@ -98,7 +70,7 @@ LL | println!("{:#?}", &arr[3..2]); LL | c(); | - mutable borrow later used here -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0502, E0503, E0506. For more information about an error, try `rustc --explain E0502`. diff --git a/tests/ui/consts/issue-65348.rs b/tests/ui/consts/issue-65348.rs index 1443fcbe1c1c..0d12da3926cd 100644 --- a/tests/ui/consts/issue-65348.rs +++ b/tests/ui/consts/issue-65348.rs @@ -9,15 +9,17 @@ impl Generic { } pub const fn array() -> &'static T { - #[allow(unconditional_panic)] + #[expect(unconditional_panic)] &Generic::::ARRAY[0] } pub const fn newtype_array() -> &'static T { + #[expect(unconditional_panic)] &Generic::::NEWTYPE_ARRAY.0[0] } pub const fn array_field() -> &'static T { + #[expect(unconditional_panic)] &(Generic::::ARRAY_FIELD.0).1[0] } diff --git a/tests/ui/stable-mir-print/operands.stdout b/tests/ui/stable-mir-print/operands.stdout index 3c27878b3cf0..c3b1151ae24a 100644 --- a/tests/ui/stable-mir-print/operands.stdout +++ b/tests/ui/stable-mir-print/operands.stdout @@ -5,187 +5,183 @@ fn operands(_1: u8) -> () { let _2: [u8; 10]; let _3: u8; let _4: usize; - let mut _5: usize; - let mut _6: bool; - let _7: u8; - let _8: usize; - let mut _9: (usize, bool); - let mut _10: usize; - let mut _11: bool; - let mut _12: (&u8, &u8); - let mut _13: &u8; - let mut _14: &u8; - let _15: &u8; - let _16: &u8; - let mut _17: bool; - let mut _18: u8; - let mut _19: u8; - let _20: core::panicking::AssertKind; - let _21: !; - let mut _22: Option>; - let _23: &u8; - let _24: u8; - let mut _25: (&u8, &u8); - let mut _26: &u8; - let mut _27: &u8; - let _28: &u8; - let _29: &u8; - let mut _30: bool; - let mut _31: u8; - let mut _32: u8; - let _33: core::panicking::AssertKind; - let _34: !; - let mut _35: Option>; - let _36: (u8, u8); - let _37: u8; - let _38: u8; - let mut _39: (&u8, &u8); - let mut _40: &u8; - let mut _41: &u8; - let _42: &u8; - let _43: &u8; - let mut _44: bool; - let mut _45: u8; - let mut _46: u8; - let _47: core::panicking::AssertKind; - let _48: !; - let mut _49: Option>; - let _50: usize; - let mut _51: &[u8]; - let mut _52: &[u8; 10]; - let _53: usize; - let _54: &usize; - let mut _55: (&usize, &usize); - let mut _56: &usize; - let mut _57: &usize; - let _58: &usize; - let _59: &usize; - let mut _60: bool; - let mut _61: usize; - let mut _62: usize; - let _63: core::panicking::AssertKind; - let _64: !; - let mut _65: Option>; + let mut _5: bool; + let _6: u8; + let _7: usize; + let mut _8: (usize, bool); + let mut _9: bool; + let mut _10: (&u8, &u8); + let mut _11: &u8; + let mut _12: &u8; + let _13: &u8; + let _14: &u8; + let mut _15: bool; + let mut _16: u8; + let mut _17: u8; + let _18: core::panicking::AssertKind; + let _19: !; + let mut _20: Option>; + let _21: &u8; + let _22: u8; + let mut _23: (&u8, &u8); + let mut _24: &u8; + let mut _25: &u8; + let _26: &u8; + let _27: &u8; + let mut _28: bool; + let mut _29: u8; + let mut _30: u8; + let _31: core::panicking::AssertKind; + let _32: !; + let mut _33: Option>; + let _34: (u8, u8); + let _35: u8; + let _36: u8; + let mut _37: (&u8, &u8); + let mut _38: &u8; + let mut _39: &u8; + let _40: &u8; + let _41: &u8; + let mut _42: bool; + let mut _43: u8; + let mut _44: u8; + let _45: core::panicking::AssertKind; + let _46: !; + let mut _47: Option>; + let _48: usize; + let mut _49: &[u8]; + let mut _50: &[u8; 10]; + let _51: usize; + let _52: &usize; + let mut _53: (&usize, &usize); + let mut _54: &usize; + let mut _55: &usize; + let _56: &usize; + let _57: &usize; + let mut _58: bool; + let mut _59: usize; + let mut _60: usize; + let _61: core::panicking::AssertKind; + let _62: !; + let mut _63: Option>; debug val => _1; debug array => _2; debug first => _3; - debug last => _7; - debug left_val => _15; - debug right_val => _16; - debug kind => _20; - debug reference => _23; - debug dereferenced => _24; - debug left_val => _28; - debug right_val => _29; - debug kind => _33; - debug tuple => _36; - debug first_again => _37; - debug first_again_again => _38; - debug left_val => _42; - debug right_val => _43; - debug kind => _47; - debug length => _50; - debug size_of => _53; - debug left_val => _58; - debug right_val => _59; - debug kind => _63; + debug last => _6; + debug left_val => _13; + debug right_val => _14; + debug kind => _18; + debug reference => _21; + debug dereferenced => _22; + debug left_val => _26; + debug right_val => _27; + debug kind => _31; + debug tuple => _34; + debug first_again => _35; + debug first_again_again => _36; + debug left_val => _40; + debug right_val => _41; + debug kind => _45; + debug length => _48; + debug size_of => _51; + debug left_val => _56; + debug right_val => _57; + debug kind => _61; bb0: { _2 = [_1; 10]; _4 = 0_usize; - _5 = 10_usize; - _6 = Lt(_4, _5); - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable]; + _5 = Lt(_4, 10_usize); + assert(move _5, "index out of bounds: the length is {} but the index is {}", 10_usize, _4) -> [success: bb1, unwind unreachable]; } bb1: { _3 = _2[_4]; - _9 = CheckedSub(10_usize, 1_usize); - assert(!move (_9.1: bool), "attempt to compute `{} - {}`, which would overflow", 10_usize, 1_usize) -> [success: bb2, unwind unreachable]; + _8 = CheckedSub(10_usize, 1_usize); + assert(!move (_8.1: bool), "attempt to compute `{} - {}`, which would overflow", 10_usize, 1_usize) -> [success: bb2, unwind unreachable]; } bb2: { - _8 = move (_9.0: usize); - _10 = 10_usize; - _11 = Lt(_8, _10); - assert(move _11, "index out of bounds: the length is {} but the index is {}", move _10, _8) -> [success: bb3, unwind unreachable]; + _7 = move (_8.0: usize); + _9 = Lt(_7, 10_usize); + assert(move _9, "index out of bounds: the length is {} but the index is {}", 10_usize, _7) -> [success: bb3, unwind unreachable]; } bb3: { - _7 = _2[_8]; - _13 = &_3; - _14 = &_7; - _12 = (move _13, move _14); - _15 = (_12.0: &u8); - _16 = (_12.1: &u8); - _18 = (*_15); - _19 = (*_16); - _17 = Eq(move _18, move _19); - switchInt(move _17) -> [0: bb5, otherwise: bb4]; + _6 = _2[_7]; + _11 = &_3; + _12 = &_6; + _10 = (move _11, move _12); + _13 = (_10.0: &u8); + _14 = (_10.1: &u8); + _16 = (*_13); + _17 = (*_14); + _15 = Eq(move _16, move _17); + switchInt(move _15) -> [0: bb5, otherwise: bb4]; } bb4: { - _23 = &_3; - _24 = (*_23); - _26 = &_24; - _27 = &_3; - _25 = (move _26, move _27); - _28 = (_25.0: &u8); - _29 = (_25.1: &u8); - _31 = (*_28); - _32 = (*_29); - _30 = Eq(move _31, move _32); - switchInt(move _30) -> [0: bb7, otherwise: bb6]; + _21 = &_3; + _22 = (*_21); + _24 = &_22; + _25 = &_3; + _23 = (move _24, move _25); + _26 = (_23.0: &u8); + _27 = (_23.1: &u8); + _29 = (*_26); + _30 = (*_27); + _28 = Eq(move _29, move _30); + switchInt(move _28) -> [0: bb7, otherwise: bb6]; } bb5: { - _20 = core::panicking::AssertKind::Eq; - _22 = std::option::Option::None; - _21 = core::panicking::assert_failed::(move _20, _15, _16, move _22) -> unwind unreachable; + _18 = core::panicking::AssertKind::Eq; + _20 = std::option::Option::None; + _19 = core::panicking::assert_failed::(move _18, _13, _14, move _20) -> unwind unreachable; } bb6: { - _36 = (_3, _7); - _37 = (_36.0: u8); - _38 = (_36.0: u8); - _40 = &_37; - _41 = &_38; - _39 = (move _40, move _41); - _42 = (_39.0: &u8); - _43 = (_39.1: &u8); - _45 = (*_42); - _46 = (*_43); - _44 = Eq(move _45, move _46); - switchInt(move _44) -> [0: bb9, otherwise: bb8]; + _34 = (_3, _6); + _35 = (_34.0: u8); + _36 = (_34.0: u8); + _38 = &_35; + _39 = &_36; + _37 = (move _38, move _39); + _40 = (_37.0: &u8); + _41 = (_37.1: &u8); + _43 = (*_40); + _44 = (*_41); + _42 = Eq(move _43, move _44); + switchInt(move _42) -> [0: bb9, otherwise: bb8]; } bb7: { - _33 = core::panicking::AssertKind::Eq; - _35 = std::option::Option::None; - _34 = core::panicking::assert_failed::(move _33, _28, _29, move _35) -> unwind unreachable; + _31 = core::panicking::AssertKind::Eq; + _33 = std::option::Option::None; + _32 = core::panicking::assert_failed::(move _31, _26, _27, move _33) -> unwind unreachable; } bb8: { - _52 = &_2; - _51 = move _52 as &[u8]; - _50 = PtrMetadata(move _51); - _54 = &_50; - _53 = std::mem::size_of_val::(_54) -> [return: bb10, unwind unreachable]; + _50 = &_2; + _49 = move _50 as &[u8]; + _48 = PtrMetadata(move _49); + _52 = &_48; + _51 = std::mem::size_of_val::(_52) -> [return: bb10, unwind unreachable]; } bb9: { - _47 = core::panicking::AssertKind::Eq; - _49 = std::option::Option::None; - _48 = core::panicking::assert_failed::(move _47, _42, _43, move _49) -> unwind unreachable; + _45 = core::panicking::AssertKind::Eq; + _47 = std::option::Option::None; + _46 = core::panicking::assert_failed::(move _45, _40, _41, move _47) -> unwind unreachable; } bb10: { - _56 = &_50; - _57 = &_53; - _55 = (move _56, move _57); - _58 = (_55.0: &usize); - _59 = (_55.1: &usize); - _61 = (*_58); - _62 = (*_59); - _60 = Eq(move _61, move _62); - switchInt(move _60) -> [0: bb12, otherwise: bb11]; + _54 = &_48; + _55 = &_51; + _53 = (move _54, move _55); + _56 = (_53.0: &usize); + _57 = (_53.1: &usize); + _59 = (*_56); + _60 = (*_57); + _58 = Eq(move _59, move _60); + switchInt(move _58) -> [0: bb12, otherwise: bb11]; } bb11: { return; } bb12: { - _63 = core::panicking::AssertKind::Eq; - _65 = std::option::Option::None; - _64 = core::panicking::assert_failed::(move _63, _58, _59, move _65) -> unwind unreachable; + _61 = core::panicking::AssertKind::Eq; + _63 = std::option::Option::None; + _62 = core::panicking::assert_failed::(move _61, _56, _57, move _63) -> unwind unreachable; } } fn operands::{constant#0}() -> usize { From eeecb56b7301c1bf1cf221c65e857b756b9ab42b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 Jan 2025 22:45:03 +0000 Subject: [PATCH 185/342] Represent the raw pointer for a array length check as a new kind of fake borrow --- compiler/rustc_borrowck/src/lib.rs | 11 ++-- .../src/polonius/legacy/loan_invalidations.rs | 21 ++++--- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 7 ++- .../src/check_consts/check.rs | 18 +++--- .../rustc_const_eval/src/interpret/step.rs | 18 ++---- compiler/rustc_middle/src/mir/syntax.rs | 55 ++++++++++++++++++- compiler/rustc_middle/src/mir/tcx.rs | 4 +- .../rustc_middle/src/mir/type_foldable.rs | 1 + compiler/rustc_middle/src/mir/visit.rs | 7 ++- .../src/builder/custom/parse/instruction.rs | 2 +- .../src/builder/expr/as_place.rs | 19 ++----- .../rustc_mir_build/src/builder/expr/into.rs | 2 +- .../rustc_mir_dataflow/src/elaborate_drops.rs | 4 +- compiler/rustc_mir_transform/src/gvn.rs | 6 +- .../rustc_mir_transform/src/large_enums.rs | 4 +- .../src/shim/async_destructor_ctor.rs | 12 +--- .../rustc_smir/src/rustc_smir/convert/mir.rs | 12 ++++ compiler/rustc_span/src/hygiene.rs | 4 -- compiler/stable_mir/src/mir/body.rs | 22 +++++++- compiler/stable_mir/src/mir/pretty.rs | 14 ++++- compiler/stable_mir/src/mir/visit.rs | 2 +- .../tests/pass/disjoint-array-accesses.rs | 35 ++++++++++++ ...ray_and_slice.index_custom.built.after.mir | 2 +- ..._and_slice.index_mut_slice.built.after.mir | 2 +- .../mir-opt/building/index_array_and_slice.rs | 2 +- tests/mir-opt/issue_91633.foo.built.after.mir | 2 +- 26 files changed, 199 insertions(+), 89 deletions(-) create mode 100644 src/tools/miri/tests/pass/disjoint-array-accesses.rs diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 91dc76f597ad..decfab502bb5 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1284,15 +1284,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &Rvalue::RawPtr(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( + &Rvalue::RawPtr(kind, place) => { + let access_kind = match kind { + RawPtrKind::Mut => ( Deep, Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::Default, })), ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::FakeForPtrMetadata => { + (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy)) + } }; self.access_place( diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index cbcfab1dc3e1..f79bcf5af556 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -3,11 +3,7 @@ use std::ops::ControlFlow; use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - self, BasicBlock, Body, BorrowKind, FakeBorrowKind, InlineAsmOperand, Location, Mutability, - NonDivergingIntrinsic, Operand, Place, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, -}; +use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::debug; @@ -60,7 +56,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { self.consume_operand(location, op); } - StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { src, dst, count, @@ -273,15 +269,18 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } - &Rvalue::RawPtr(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( + &Rvalue::RawPtr(kind, place) => { + let access_kind = match kind { + RawPtrKind::Mut => ( Deep, Write(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: mir::MutBorrowKind::Default, + kind: MutBorrowKind::Default, })), ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::FakeForPtrMetadata => { + (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy)) + } }; self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e775d219c7b4..d7fc5e8e673f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -612,9 +612,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::CopyForDeref(place) => { self.codegen_operand(bx, &mir::Operand::Copy(place)) } - mir::Rvalue::RawPtr(mutability, place) => { - let mk_ptr = - move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| Ty::new_ptr(tcx, ty, mutability); + mir::Rvalue::RawPtr(kind, place) => { + let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { + Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy()) + }; self.codegen_place_to_pointer(bx, place, mk_ptr) } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index c3b609b642df..83850aef3019 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -518,7 +518,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Ref(_, BorrowKind::Mut { .. }, place) - | Rvalue::RawPtr(Mutability::Mut, place) => { + | Rvalue::RawPtr(RawPtrKind::Mut, place) => { // Inside mutable statics, we allow arbitrary mutable references. // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact // reasons why are lost to history), and there is no reason to restrict that to @@ -536,7 +536,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place) - | Rvalue::RawPtr(Mutability::Not, place) => { + | Rvalue::RawPtr(RawPtrKind::Const, place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( self.ccx, &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), @@ -548,6 +548,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => { + // These are only inserted for slice length, so the place must already be indirect. + // This implies we do not have to worry about whether the borrow escapes. + assert!(place.is_indirect(), "fake borrows are always indirect"); + } + Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::MutToConstPointer @@ -600,12 +606,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } UnOp::PtrMetadata => { - if !ty.is_ref() && !ty.is_unsafe_ptr() { - span_bug!( - self.span, - "non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}", - ); - } + // Getting the metadata from a pointer is always const. + // We already validated the type is valid in the validator. } } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index a26c2eca107c..d9c0ff5acd11 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,7 +9,6 @@ use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::source_map::Spanned; -use rustc_span::{DesugaringKind, Span}; use rustc_target::callconv::FnAbi; use tracing::{info, instrument, trace}; @@ -81,9 +80,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { use rustc_middle::mir::StatementKind::*; match &stmt.kind { - Assign(box (place, rvalue)) => { - self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)? - } + Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?, SetDiscriminant { place, variant_index } => { let dest = self.eval_place(**place)?; @@ -162,7 +159,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, rvalue: &mir::Rvalue<'tcx>, place: mir::Place<'tcx>, - span: Span, ) -> InterpResult<'tcx> { let dest = self.eval_place(place)?; // FIXME: ensure some kind of non-aliasing between LHS and RHS? @@ -241,7 +237,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_immediate(*val, &dest)?; } - RawPtr(_, place) => { + RawPtr(kind, place) => { // Figure out whether this is an addr_of of an already raw place. let place_base_raw = if place.is_indirect_first_projection() { let ty = self.frame().body.local_decls[place.local].ty; @@ -254,13 +250,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout); - if !place_base_raw - && span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow) - { - // If this was not already raw, it needs retagging. - // As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)` - // from indexing. (Really we should not do any retag on `&raw` but that does not - // currently work with Stacked Borrows.) + if !place_base_raw && !kind.is_fake() { + // If this was not already raw, it needs retagging -- except for "fake" + // raw borrows whose defining property is that they do not get retagged. val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?; } self.write_immediate(*val, &dest)?; diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 29ae2e1bd6bc..5868b64f6b5c 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -180,6 +180,59 @@ pub enum BorrowKind { Mut { kind: MutBorrowKind }, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] +pub enum RawPtrKind { + Mut, + Const, + /// Creates a raw pointer to a place that will only be used to access its metadata, + /// not the data behind the pointer. Note that this limitation is *not* enforced + /// by the validator. + /// + /// The borrow checker allows overlap of these raw pointers with references to the + /// data. This is sound even if the pointer is "misused" since any such use is anyway + /// unsafe. In terms of the operational semantics (i.e., Miri), this is equivalent + /// to `RawPtrKind::Mut`, but will never incur a retag. + FakeForPtrMetadata, +} + +impl From for RawPtrKind { + fn from(other: Mutability) -> Self { + match other { + Mutability::Mut => RawPtrKind::Mut, + Mutability::Not => RawPtrKind::Const, + } + } +} + +impl RawPtrKind { + pub fn is_fake(self) -> bool { + match self { + RawPtrKind::Mut | RawPtrKind::Const => false, + RawPtrKind::FakeForPtrMetadata => true, + } + } + + pub fn to_mutbl_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + + // We have no type corresponding to a fake borrow, so use + // `*const` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } + + pub fn ptr_str(self) -> &'static str { + match self { + RawPtrKind::Mut => "mut", + RawPtrKind::Const => "const", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(Hash, HashStable)] pub enum MutBorrowKind { @@ -1356,7 +1409,7 @@ pub enum Rvalue<'tcx> { /// /// Like with references, the semantics of this operation are heavily dependent on the aliasing /// model. - RawPtr(Mutability, Place<'tcx>), + RawPtr(RawPtrKind, Place<'tcx>), /// Yields the length of the place, as a `usize`. /// diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index db77017310af..4d11492e94d3 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -206,9 +206,9 @@ impl<'tcx> Rvalue<'tcx> { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } - Rvalue::RawPtr(mutability, ref place) => { + Rvalue::RawPtr(kind, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ptr(tcx, place_ty, mutability) + Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) } Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index b798f0788007..b59b9e55fe8a 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -15,6 +15,7 @@ TrivialTypeTraversalImpls! { SourceScopeLocalData, UserTypeAnnotationIndex, BorrowKind, + RawPtrKind, CastKind, BasicBlock, SwitchTargets, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 058acbd4024d..95de08ce9c87 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -685,12 +685,15 @@ macro_rules! make_mir_visitor { Rvalue::RawPtr(m, path) => { let ctx = match m { - Mutability::Mut => PlaceContext::MutatingUse( + RawPtrKind::Mut => PlaceContext::MutatingUse( MutatingUseContext::RawBorrow ), - Mutability::Not => PlaceContext::NonMutatingUse( + RawPtrKind::Const => PlaceContext::NonMutatingUse( NonMutatingUseContext::RawBorrow ), + RawPtrKind::FakeForPtrMetadata => PlaceContext::NonMutatingUse( + NonMutatingUseContext::Inspect + ), }; self.visit_place(path, ctx, location); } diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 59f440432ebc..eab414e150fa 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -253,7 +253,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) ), ExprKind::RawBorrow { mutability, arg } => Ok( - Rvalue::RawPtr(*mutability, self.parse_place(*arg)?) + Rvalue::RawPtr((*mutability).into(), self.parse_place(*arg)?) ), ExprKind::Binary { op, lhs, rhs } => Ok( Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 7634bc74fcbb..0086775e9f46 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance}; use rustc_middle::{bug, span_bug}; -use rustc_span::{DesugaringKind, Span}; +use rustc_span::Span; use tracing::{debug, instrument, trace}; use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard}; @@ -643,8 +643,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, ) -> Operand<'tcx> { let place_ty = place.ty(&self.local_decls, self.tcx).ty; - let usize_ty = self.tcx.types.usize; - match place_ty.kind() { ty::Array(_elem_ty, len_const) => { // We know how long an array is, so just use that as a constant @@ -668,27 +666,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the MIR we're building here needs to pass NLL later. Operand::Copy(Place::from(place.local)) } else { - let len_span = self.tcx.with_stable_hashing_context(|hcx| { - let span = source_info.span; - span.mark_with_reason( - None, - DesugaringKind::IndexBoundsCheckReborrow, - span.edition(), - hcx, - ) - }); let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); let slice_ptr = self.temp(ptr_ty, span); self.cfg.push_assign( block, - SourceInfo { span: len_span, ..source_info }, + source_info, slice_ptr, - Rvalue::RawPtr(Mutability::Not, place), + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place), ); Operand::Move(slice_ptr) }; - let len = self.temp(usize_ty, span); + let len = self.temp(self.tcx.types.usize, span); self.cfg.push_assign( block, source_info, diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 88f63d4e22cb..928156572d50 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -303,7 +303,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { hir::Mutability::Not => this.as_read_only_place(block, arg), hir::Mutability::Mut => this.as_place(block, arg), }; - let address_of = Rvalue::RawPtr(mutability, unpack!(block = place)); + let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place)); this.cfg.push_assign(block, source_info, destination, address_of); block.unit() } diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index f8a846749475..74b0e84068c6 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -700,7 +700,7 @@ where statements: vec![ self.assign( ptr, - Rvalue::RawPtr(Mutability::Mut, tcx.mk_place_index(self.place, cur)), + Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_index(self.place, cur)), ), self.assign( cur.into(), @@ -816,7 +816,7 @@ where let mut delegate_block = BasicBlockData { statements: vec![ - self.assign(Place::from(array_ptr), Rvalue::RawPtr(Mutability::Mut, self.place)), + self.assign(Place::from(array_ptr), Rvalue::RawPtr(RawPtrKind::Mut, self.place)), self.assign( Place::from(slice_ptr), Rvalue::Cast( diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 1c2c82d4cd57..16e15fa12e07 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -192,7 +192,7 @@ enum AggregateTy<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), - Address(Mutability), + Address(RawPtrKind), } #[derive(Debug, PartialEq, Eq, Hash)] @@ -504,7 +504,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { mplace.layout.ty, bk.to_mutbl_lossy(), ), - AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), + AddressKind::Address(mutbl) => { + Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy()) + } }; let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index e201763468b3..1e546bfbeb30 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -125,7 +125,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { source_info, kind: StatementKind::Assign(Box::new(( dst, - Rvalue::RawPtr(Mutability::Mut, *lhs), + Rvalue::RawPtr(RawPtrKind::Mut, *lhs), ))), }; @@ -146,7 +146,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { source_info, kind: StatementKind::Assign(Box::new(( src, - Rvalue::RawPtr(Mutability::Not, *rhs), + Rvalue::RawPtr(RawPtrKind::Const, *rhs), ))), }; diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index f01bab75c4a1..1d53440cf0ba 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -2,17 +2,11 @@ use std::iter; use itertools::Itertools; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_ast::Mutability; use rustc_const_eval::interpret; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, CallSource, CastKind, CoercionSource, Const, ConstOperand, - ConstValue, Local, LocalDecl, MirSource, Operand, Place, PlaceElem, RETURN_PLACE, Rvalue, - SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, - UnwindTerminateReason, -}; +use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -345,7 +339,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { .tcx .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), }; - self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) + self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) } /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of @@ -365,7 +359,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { PlaceElem::Field(field, field_ty), ]), }; - self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) + self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) } /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index a5a17b4b5730..aee98d7d410e 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -232,6 +232,18 @@ impl<'tcx> Stable<'tcx> for mir::Mutability { } } +impl<'tcx> Stable<'tcx> for mir::RawPtrKind { + type T = stable_mir::mir::RawPtrKind; + fn stable(&self, _: &mut Tables<'_>) -> Self::T { + use mir::RawPtrKind::*; + match *self { + Const => stable_mir::mir::RawPtrKind::Const, + Mut => stable_mir::mir::RawPtrKind::Mut, + FakeForPtrMetadata => stable_mir::mir::RawPtrKind::FakeForPtrMetadata, + } + } +} + impl<'tcx> Stable<'tcx> for mir::BorrowKind { type T = stable_mir::mir::BorrowKind; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3cdae437b7d4..a5826137181d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1163,9 +1163,6 @@ pub enum DesugaringKind { WhileLoop, /// `async Fn()` bound modifier BoundModifier, - /// Marks a `&raw const *_1` needed as part of getting the length of a mutable - /// slice for the bounds check, so that MIRI's retag handling can recognize it. - IndexBoundsCheckReborrow, } impl DesugaringKind { @@ -1182,7 +1179,6 @@ impl DesugaringKind { DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", - DesugaringKind::IndexBoundsCheckReborrow => "slice indexing", } } } diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index dfd090b39563..8686169c15d6 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -457,7 +457,7 @@ pub enum Rvalue { /// /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like /// `&raw v` or `addr_of!(v)`. - AddressOf(Mutability, Place), + AddressOf(RawPtrKind, Place), /// Creates an aggregate value, like a tuple or struct. /// @@ -577,7 +577,7 @@ impl Rvalue { } Rvalue::AddressOf(mutability, place) => { let place_ty = place.ty(locals)?; - Ok(Ty::new_ptr(place_ty, *mutability)) + Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) } Rvalue::Len(..) => Ok(Ty::usize_ty()), Rvalue::Cast(.., ty) => Ok(*ty), @@ -903,6 +903,24 @@ impl BorrowKind { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RawPtrKind { + Mut, + Const, + FakeForPtrMetadata, +} + +impl RawPtrKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut { .. } => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum MutBorrowKind { Default, diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 93ed32e258a8..81981bce2026 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -6,7 +6,9 @@ use std::{fmt, io, iter}; use fmt::{Display, Formatter}; use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; -use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; +use crate::mir::{ + Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, +}; use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst}; use crate::{Body, CrateDef, Mutability, with}; @@ -325,7 +327,7 @@ fn pretty_ty_const(ct: &TyConst) -> String { fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { match rval { Rvalue::AddressOf(mutability, place) => { - write!(writer, "&raw {} {:?}", pretty_mut(*mutability), place) + write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) } Rvalue::Aggregate(aggregate_kind, operands) => { // FIXME: Add pretty_aggregate function that returns a pretty string @@ -437,3 +439,11 @@ fn pretty_mut(mutability: Mutability) -> &'static str { Mutability::Mut => "mut ", } } + +fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { + match kind { + RawPtrKind::Const => "const", + RawPtrKind::Mut => "mut", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } +} diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index d73e79c48937..79efb83cebde 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -308,7 +308,7 @@ pub trait MirVisitor { fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { match rvalue { Rvalue::AddressOf(mutability, place) => { - let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut }; + let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; self.visit_place(place, pcx, location); } Rvalue::Aggregate(_, operands) => { diff --git a/src/tools/miri/tests/pass/disjoint-array-accesses.rs b/src/tools/miri/tests/pass/disjoint-array-accesses.rs new file mode 100644 index 000000000000..50d0ea52ad4a --- /dev/null +++ b/src/tools/miri/tests/pass/disjoint-array-accesses.rs @@ -0,0 +1,35 @@ +// This is a regression test for issue #135671 where a MIR refactor about arrays and their lengths +// unexpectedly caused borrowck errors for disjoint borrows of array elements, for which we had no +// tests. This is a collection of a few code samples from that issue. + +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +struct Test { + a: i32, + b: i32, +} + +fn one() { + let inputs: &mut [_] = &mut [Test { a: 0, b: 0 }]; + let a = &mut inputs[0].a; + let b = &mut inputs[0].b; + + *a = 0; + *b = 1; +} + +fn two() { + let slice = &mut [(0, 0)][..]; + std::mem::swap(&mut slice[0].0, &mut slice[0].1); +} + +fn three(a: &mut [(i32, i32)], i: usize, j: usize) -> (&mut i32, &mut i32) { + (&mut a[i].0, &mut a[j].1) +} + +fn main() { + one(); + two(); + three(&mut [(1, 2), (3, 4)], 0, 1); +} diff --git a/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir index 00f2b7e07d55..e6745ddbf298 100644 --- a/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir +++ b/tests/mir-opt/building/index_array_and_slice.index_custom.built.after.mir @@ -14,7 +14,7 @@ fn index_custom(_1: &WithSliceTail, _2: usize) -> &i32 { StorageLive(_3); StorageLive(_4); _4 = copy _2; - _5 = &raw const ((*_1).1: [i32]); + _5 = &raw const (fake) ((*_1).1: [i32]); _6 = PtrMetadata(move _5); _7 = Lt(copy _4, copy _6); assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2]; diff --git a/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir b/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir index cb0b2f600c81..c96bcdfc918a 100644 --- a/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir +++ b/tests/mir-opt/building/index_array_and_slice.index_mut_slice.built.after.mir @@ -14,7 +14,7 @@ fn index_mut_slice(_1: &mut [i32], _2: usize) -> &i32 { StorageLive(_3); StorageLive(_4); _4 = copy _2; - _5 = &raw const (*_1); + _5 = &raw const (fake) (*_1); _6 = PtrMetadata(move _5); _7 = Lt(copy _4, copy _6); assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb2]; diff --git a/tests/mir-opt/building/index_array_and_slice.rs b/tests/mir-opt/building/index_array_and_slice.rs index 16d0b983132d..f91b37567f79 100644 --- a/tests/mir-opt/building/index_array_and_slice.rs +++ b/tests/mir-opt/building/index_array_and_slice.rs @@ -54,7 +54,7 @@ struct WithSliceTail(f64, [i32]); // EMIT_MIR index_array_and_slice.index_custom.built.after.mir fn index_custom(custom: &WithSliceTail, index: usize) -> &i32 { // CHECK: bb0: - // CHECK: [[PTR:_.+]] = &raw const ((*_1).1: [i32]); + // CHECK: [[PTR:_.+]] = &raw const (fake) ((*_1).1: [i32]); // CHECK: [[LEN:_.+]] = PtrMetadata(move [[PTR]]); // CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]); // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1, diff --git a/tests/mir-opt/issue_91633.foo.built.after.mir b/tests/mir-opt/issue_91633.foo.built.after.mir index bf65b5b4a8cc..53f48350596e 100644 --- a/tests/mir-opt/issue_91633.foo.built.after.mir +++ b/tests/mir-opt/issue_91633.foo.built.after.mir @@ -18,7 +18,7 @@ fn foo(_1: Box<[T]>) -> T { StorageLive(_3); StorageLive(_4); _4 = const 0_usize; - _5 = &raw const (*_1); + _5 = &raw const (fake) (*_1); _6 = PtrMetadata(move _5); _7 = Lt(copy _4, copy _6); assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, copy _4) -> [success: bb1, unwind: bb5]; From bc135aaa9815557704978ccf9ee15aed49f2f976 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 28 Jan 2025 02:56:32 +0100 Subject: [PATCH 186/342] interpret: is_alloc_live: check global allocs last --- compiler/rustc_const_eval/src/interpret/memory.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2772c94d52b0..d736f14f5a38 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -830,9 +830,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is /// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics. pub fn is_alloc_live(&self, id: AllocId) -> bool { - self.tcx.try_get_global_alloc(id).is_some() - || self.memory.alloc_map.contains_key_ref(&id) + self.memory.alloc_map.contains_key_ref(&id) || self.memory.extra_fn_ptr_map.contains_key(&id) + // We check `tcx` last as that has to acquire a lock in `many-seeds` mode. + // This also matches the order in `get_alloc_info`. + || self.tcx.try_get_global_alloc(id).is_some() } /// Obtain the size and alignment of an allocation, even if that allocation has From d94b64dcefcdf4ad29e8d5f8240389d9506968e5 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 13 Jan 2025 15:20:36 -0700 Subject: [PATCH 187/342] rustdoc: add nobuild typescript checking to our JS By nobuild, I mean that the type annotations are all in comments, not in the "native" typescript syntax. This is a bit uglier, but it lets you rapid-prototype without tsc, works with all the native browser debugging tools, and keeps Node out of Rust's bootstrap chain. This pull request mostly just adds ts-ignore annotations and type declarations. To actually take good advantage of typescript, we'll want to "burn down" this pile of unsafe code until we eventually have a version with almost none of these. This PR also adds tsc to the mingw-check Dockerfile, so that it can't fall out of date like the Closure annotations did. https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/typescript --- .../docker/host-x86_64/mingw-check/Dockerfile | 5 +- src/librustdoc/html/static/js/README.md | 10 +- src/librustdoc/html/static/js/externs.js | 270 ---- src/librustdoc/html/static/js/main.js | 345 ++++- src/librustdoc/html/static/js/rustdoc.d.ts | 387 ++++++ .../html/static/js/scrape-examples.js | 3 + src/librustdoc/html/static/js/search.js | 1201 ++++++++++++----- src/librustdoc/html/static/js/settings.js | 3 + src/librustdoc/html/static/js/src-script.js | 3 + src/librustdoc/html/static/js/storage.js | 98 +- src/librustdoc/html/static/js/tsconfig.json | 15 + 11 files changed, 1658 insertions(+), 682 deletions(-) delete mode 100644 src/librustdoc/html/static/js/externs.js create mode 100644 src/librustdoc/html/static/js/rustdoc.d.ts create mode 100644 src/librustdoc/html/static/js/tsconfig.json diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d408cd518a00..9234c6dc921e 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -29,7 +29,7 @@ ENV PATH="/node/bin:${PATH}" # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. -RUN npm install es-check@6.1.1 eslint@8.6.0 -g +RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -68,4 +68,5 @@ ENV SCRIPT \ es-check es2019 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ - eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js + eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js && \ + tsc --project ../src/librustdoc/html/static/js/tsconfig.json diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md index 1fd859ad7cf4..e99d7330f0ed 100644 --- a/src/librustdoc/html/static/js/README.md +++ b/src/librustdoc/html/static/js/README.md @@ -3,13 +3,9 @@ These JavaScript files are incorporated into the rustdoc binary at build time, and are minified and written to the filesystem as part of the doc build process. -We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler) +We use the [TypeScript Compiler](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) dialect of JSDoc to comment our code and annotate params and return types. To run a check: - ./x.py doc library/std - npm i -g google-closure-compiler - google-closure-compiler -W VERBOSE \ - build//doc/{search-index*.js,crates*.js} \ - src/librustdoc/html/static/js/{search.js,main.js,storage.js} \ - --externs src/librustdoc/html/static/js/externs.js >/dev/null + npm i -g typescript + tsc --project tsconfig.json diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js deleted file mode 100644 index c4faca1c0c3b..000000000000 --- a/src/librustdoc/html/static/js/externs.js +++ /dev/null @@ -1,270 +0,0 @@ -// This file contains type definitions that are processed by the Closure Compiler but are -// not put into the JavaScript we include as part of the documentation. It is used for -// type checking. See README.md in this directory for more info. - -/* eslint-disable */ -let searchState; -function initSearch(searchIndex){} - -/** - * @typedef {{ - * name: string, - * id: number|null, - * fullPath: Array, - * pathWithoutLast: Array, - * pathLast: string, - * generics: Array, - * bindings: Map>, - * }} - */ -let QueryElement; - -/** - * @typedef {{ - * pos: number, - * totalElems: number, - * typeFilter: (null|string), - * userQuery: string, - * isInBinding: (null|string), - * }} - */ -let ParserState; - -/** - * @typedef {{ - * original: string, - * userQuery: string, - * typeFilter: number, - * elems: Array, - * args: Array, - * returned: Array, - * foundElems: number, - * totalElems: number, - * literalSearch: boolean, - * hasReturnArrow: boolean, - * corrections: Array<{from: string, to: integer}> | null, - * typeFingerprint: Uint32Array, - * error: Array | null, - * }} - */ -let ParsedQuery; - -/** - * @typedef {{ - * crate: string, - * desc: string, - * id: number, - * name: string, - * normalizedName: string, - * parent: (Object|null|undefined), - * path: string, - * ty: (Number|null|number), - * type: FunctionSearchType? - * }} - */ -let Row; - -/** - * @typedef {{ - * in_args: Array, - * returned: Array, - * others: Array, - * query: ParsedQuery, - * }} - */ -let ResultsTable; - -/** - * @typedef {Map} - */ -let Results; - -/** - * @typedef {{ - * desc: string, - * displayPath: string, - * fullPath: string, - * href: string, - * id: number, - * lev: number, - * name: string, - * normalizedName: string, - * parent: (Object|undefined), - * path: string, - * ty: number, - * type: FunctionSearchType?, - * displayType: Promise>>|null, - * displayTypeMappedNames: Promise]>>|null, - * }} - */ -let ResultObject; - -/** - * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. - * The JavaScript deserializes this into FunctionSearchType. - * - * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` - * because `null` is four bytes while `0` is one byte. - * - * An input or output can be encoded as just a number if there is only one of them, AND - * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had - * a function with a single output, and that output had a single generic: - * - * fn something() -> Result - * - * If output was allowed to be any RawFunctionType, it would look like thi - * - * [[], [50, [3, 3]]] - * - * The problem is that the above output could be interpreted as either a type with ID 50 and two - * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second - * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing - * in favor of the pair of types interpretation. This is why the `(number|Array)` - * is used instead of `(RawFunctionType|Array)`. - * - * The output can be skipped if it's actually unit and there's no type constraints. If thi - * function accepts constrained generics, then the output will be unconditionally emitted, and - * after it will come a list of trait constraints. The position of the item in the list will - * determine which type parameter it is. For example: - * - * [1, 2, 3, 4, 5] - * ^ ^ ^ ^ ^ - * | | | | - generic parameter (-3) of trait 5 - * | | | - generic parameter (-2) of trait 4 - * | | - generic parameter (-1) of trait 3 - * | - this function returns a single value (type 2) - * - this function takes a single input parameter (type 1) - * - * Or, for a less contrived version: - * - * [[[4, -1], 3], [[5, -1]], 11] - * -^^^^^^^---- ^^^^^^^ ^^ - * | | | - generic parameter, roughly `where -1: 11` - * | | | since -1 is the type parameter and 11 the trait - * | | - function output 5<-1> - * | - the overall function signature is something like - * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` - * - function input, corresponds roughly to 4<-1> - * 4 is an index into the `p` array for a type - * -1 is the generic parameter, given by 11 - * - * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like - * function inputs and outputs: - * - * [-1, -1, [4, 3]] - * ^^^^^^ where -1: 4 + 3 - * - * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array - * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in - * favor of `4 + 3`: - * - * [-1, -1, [[4, 3]]] - * ^^^^^^^^ where -1: 4 + 3 - * - * [-1, -1, [5, [4, 3]]] - * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 - * - * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i - * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. - * - * @typedef {( - * 0 | - * [(number|Array)] | - * [(number|Array), (number|Array)] | - * Array<(number|Array)> - * )} - */ -let RawFunctionSearchType; - -/** - * A single function input or output type. This is either a single path ID, or a pair of - * [path ID, generics]. - * - * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` - * because `null` is four bytes while `0` is one byte. - * - * @typedef {number | [number, Array]} - */ -let RawFunctionType; - -/** - * @typedef {{ - * inputs: Array, - * output: Array, - * where_clause: Array>, - * }} - */ -let FunctionSearchType; - -/** - * @typedef {{ - * id: (null|number), - * ty: number, - * generics: Array, - * bindings: Map>, - * }} - */ -let FunctionType; - -/** - * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` - * are arrays with the same length. `q`, `a`, and `c` use a sparse - * representation for compactness. - * - * `n[i]` contains the name of an item. - * - * `t[i]` contains the type of that item - * (as a string of characters that represent an offset in `itemTypes`). - * - * `d[i]` contains the description of that item. - * - * `q` contains the full paths of the items. For compactness, it is a set of - * (index, path) pairs used to create a map. If a given index `i` is - * not present, this indicates "same as the last index present". - * - * `i[i]` contains an item's parent, usually a module. For compactness, - * it is a set of indexes into the `p` array. - * - * `f` contains function signatures, or `0` if the item isn't a function. - * More information on how they're encoded can be found in rustc-dev-guide - * - * Functions are themselves encoded as arrays. The first item is a list of - * types representing the function's inputs, and the second list item is a list - * of types representing the function's output. Tuples are flattened. - * Types are also represented as arrays; the first item is an index into the `p` - * array, while the second is a list of types representing any generic parameters. - * - * b[i] contains an item's impl disambiguator. This is only present if an item - * is defined in an impl block and, the impl block's type has more than one associated - * item with the same name. - * - * `a` defines aliases with an Array of pairs: [name, offset], where `offset` - * points into the n/t/d/q/i/f arrays. - * - * `doc` contains the description of the crate. - * - * `p` is a list of path/type pairs. It is used for parents and function parameters. - * The first item is the type, the second is the name, the third is the visible path (if any) and - * the fourth is the canonical path used for deduplication (if any). - * - * `r` is the canonical path used for deduplication of re-exported items. - * It is not used for associated items like methods (that's the fourth element - * of `p`) but is used for modules items like free functions. - * - * `c` is an array of item indices that are deprecated. - * @typedef {{ - * doc: string, - * a: Object, - * n: Array, - * t: string, - * d: Array, - * q: Array<[number, string]>, - * i: Array, - * f: string, - * p: Array<[number, string] | [number, string, number] | [number, string, number, number]>, - * b: Array<[number, String]>, - * c: Array, - * r: Array<[number, number]>, - * }} - */ -let RawSearchIndexCrate; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 984b0877d8de..ccf4002bb300 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -11,8 +11,13 @@ window.RUSTDOC_TOOLTIP_HOVER_MS = 300; window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS = 450; -// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL -// for a resource under the root-path, with the resource-suffix. +/** + * Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL + * for a resource under the root-path, with the resource-suffix. + * + * @param {string} basename + * @param {string} extension + */ function resourcePath(basename, extension) { return getVar("root-path") + basename + getVar("resource-suffix") + extension; } @@ -27,13 +32,18 @@ function hideMain() { function showMain() { const main = document.getElementById(MAIN_ID); + if (!main) { + return; + } removeClass(main, "hidden"); const mainHeading = main.querySelector(".main-heading"); - if (mainHeading && searchState.rustdocToolbar) { - if (searchState.rustdocToolbar.parentElement) { - searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + if (mainHeading && window.searchState.rustdocToolbar) { + if (window.searchState.rustdocToolbar.parentElement) { + window.searchState.rustdocToolbar.parentElement.removeChild( + window.searchState.rustdocToolbar, + ); } - mainHeading.appendChild(searchState.rustdocToolbar); + mainHeading.appendChild(window.searchState.rustdocToolbar); } const toggle = document.getElementById("toggle-all-docs"); if (toggle) { @@ -61,16 +71,20 @@ function setMobileTopbar() { } } -// Gets the human-readable string for the virtual-key code of the -// given KeyboardEvent, ev. -// -// This function is meant as a polyfill for KeyboardEvent#key, -// since it is not supported in IE 11 or Chrome for Android. We also test for -// KeyboardEvent#keyCode because the handleShortcut handler is -// also registered for the keydown event, because Blink doesn't fire -// keypress on hitting the Escape key. -// -// So I guess you could say things are getting pretty interoperable. +/** + * Gets the human-readable string for the virtual-key code of the + * given KeyboardEvent, ev. + * + * This function is meant as a polyfill for KeyboardEvent#key, + * since it is not supported in IE 11 or Chrome for Android. We also test for + * KeyboardEvent#keyCode because the handleShortcut handler is + * also registered for the keydown event, because Blink doesn't fire + * keypress on hitting the Escape key. + * + * So I guess you could say things are getting pretty interoperable. + * + * @param {KeyboardEvent} ev + */ function getVirtualKey(ev) { if ("key" in ev && typeof ev.key !== "undefined") { return ev.key; @@ -110,6 +124,9 @@ function getNakedUrl() { * @param {HTMLElement} referenceNode */ function insertAfter(newNode, referenceNode) { + // You're not allowed to pass an element with no parent. + // I dunno how to make TS's typechecker see that. + // @ts-expect-error referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } @@ -129,6 +146,7 @@ function getOrCreateSection(id, classes) { el = document.createElement("section"); el.id = id; el.className = classes; + // @ts-expect-error insertAfter(el, document.getElementById(MAIN_ID)); } return el; @@ -159,12 +177,13 @@ function getNotDisplayedElem() { * contains the displayed element (there can be only one at the same time!). So basically, we switch * elements between the two `
` elements. * - * @param {HTMLElement} elemToDisplay + * @param {HTMLElement|null} elemToDisplay */ function switchDisplayedElement(elemToDisplay) { const el = getAlternativeDisplayElem(); if (el.children.length > 0) { + // @ts-expect-error getNotDisplayedElem().appendChild(el.firstElementChild); } if (elemToDisplay === null) { @@ -177,10 +196,14 @@ function switchDisplayedElement(elemToDisplay) { removeClass(el, "hidden"); const mainHeading = elemToDisplay.querySelector(".main-heading"); + // @ts-expect-error if (mainHeading && searchState.rustdocToolbar) { + // @ts-expect-error if (searchState.rustdocToolbar.parentElement) { + // @ts-expect-error searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); } + // @ts-expect-error mainHeading.appendChild(searchState.rustdocToolbar); } } @@ -189,6 +212,7 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } +// @ts-expect-error function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -201,6 +225,7 @@ function preLoadCss(cssUrl) { (function() { const isHelpPage = window.location.pathname.endsWith("/help.html"); + // @ts-expect-error function loadScript(url, errorCallback) { const script = document.createElement("script"); script.src = url; @@ -211,21 +236,25 @@ function preLoadCss(cssUrl) { } if (getSettingsButton()) { + // @ts-expect-error getSettingsButton().onclick = event => { if (event.ctrlKey || event.altKey || event.metaKey) { return; } + // @ts-expect-error window.hideAllModals(false); addClass(getSettingsButton(), "rotate"); event.preventDefault(); // Sending request for the CSS and the JS files at the same time so it will // hopefully be loaded when the JS will generate the settings content. + // @ts-expect-error loadScript(getVar("static-root-path") + getVar("settings-js")); // Pre-load all theme CSS files, so that switching feels seamless. // // When loading settings.html as a standalone page, the equivalent HTML is // generated in context.rs. setTimeout(() => { + // @ts-expect-error const themes = getVar("themes").split(","); for (const theme of themes) { // if there are no themes, do nothing @@ -241,6 +270,8 @@ function preLoadCss(cssUrl) { window.searchState = { rustdocToolbar: document.querySelector("rustdoc-toolbar"), loadingText: "Loading search results...", + // This will always be an HTMLInputElement, but tsc can't see that + // @ts-expect-error input: document.getElementsByClassName("search-input")[0], outputElement: () => { let el = document.getElementById("search"); @@ -263,31 +294,38 @@ function preLoadCss(cssUrl) { // tab and back preserves the element that was focused. focusedByTab: [null, null, null], clearInputTimeout: () => { - if (searchState.timeout !== null) { - clearTimeout(searchState.timeout); - searchState.timeout = null; + if (window.searchState.timeout !== null) { + clearTimeout(window.searchState.timeout); + window.searchState.timeout = null; } }, - isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, + // @ts-expect-error + isDisplayed: () => { + const outputElement = window.searchState.outputElement(); + return outputElement && + outputElement.parentElement && + outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID; + }, // Sets the focus on the search bar at the top of the page focus: () => { - searchState.input.focus(); + window.searchState.input && window.searchState.input.focus(); }, // Removes the focus from the search bar. defocus: () => { - searchState.input.blur(); + window.searchState.input && window.searchState.input.blur(); }, showResults: search => { if (search === null || typeof search === "undefined") { - search = searchState.outputElement(); + search = window.searchState.outputElement(); } switchDisplayedElement(search); - searchState.mouseMovedAfterSearch = false; - document.title = searchState.title; + // @ts-expect-error + window.searchState.mouseMovedAfterSearch = false; + document.title = window.searchState.title; }, removeQueryParameters: () => { // We change the document title. - document.title = searchState.titleBeforeSearch; + document.title = window.searchState.titleBeforeSearch; if (browserSupportsHistoryApi()) { history.replaceState(null, "", getNakedUrl() + window.location.hash); } @@ -295,9 +333,10 @@ function preLoadCss(cssUrl) { hideResults: () => { switchDisplayedElement(null); // We also remove the query parameter from the URL. - searchState.removeQueryParameters(); + window.searchState.removeQueryParameters(); }, getQueryStringParams: () => { + /** @type {Object.} */ const params = {}; window.location.search.substring(1).split("&"). map(s => { @@ -309,26 +348,28 @@ function preLoadCss(cssUrl) { return params; }, setup: () => { - const search_input = searchState.input; - if (!searchState.input) { + const search_input = window.searchState.input; + if (!search_input) { return; } let searchLoaded = false; // If you're browsing the nightly docs, the page might need to be refreshed for the // search to work because the hash of the JS scripts might have changed. function sendSearchForm() { + // @ts-expect-error document.getElementsByClassName("search-form")[0].submit(); } function loadSearch() { if (!searchLoaded) { searchLoaded = true; + // @ts-expect-error loadScript(getVar("static-root-path") + getVar("search-js"), sendSearchForm); loadScript(resourcePath("search-index", ".js"), sendSearchForm); } } search_input.addEventListener("focus", () => { - search_input.origPlaceholder = search_input.placeholder; + window.searchState.origPlaceholder = search_input.placeholder; search_input.placeholder = "Type your search here."; loadSearch(); }); @@ -337,16 +378,21 @@ function preLoadCss(cssUrl) { loadSearch(); } - const params = searchState.getQueryStringParams(); + const params = window.searchState.getQueryStringParams(); if (params.search !== undefined) { - searchState.setLoadingSearch(); + window.searchState.setLoadingSearch(); loadSearch(); } }, setLoadingSearch: () => { - const search = searchState.outputElement(); - search.innerHTML = "

" + searchState.loadingText + "

"; - searchState.showResults(search); + const search = window.searchState.outputElement(); + if (!search) { + return; + } + search.innerHTML = "

" + + window.searchState.loadingText + + "

"; + window.searchState.showResults(search); }, descShards: new Map(), loadDesc: async function({descShard, descIndex}) { @@ -370,6 +416,8 @@ function preLoadCss(cssUrl) { return list[descIndex]; }, loadedDescShard: function(crate, shard, data) { + // If loadedDescShard gets called, then the library must have been declared. + // @ts-expect-error this.descShards.get(crate)[shard].resolve(data.split("\n")); }, }; @@ -377,8 +425,11 @@ function preLoadCss(cssUrl) { const toggleAllDocsId = "toggle-all-docs"; let savedHash = ""; + /** + * @param {HashChangeEvent|null} ev + */ function handleHashes(ev) { - if (ev !== null && searchState.isDisplayed() && ev.newURL) { + if (ev !== null && window.searchState.isDisplayed() && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. switchDisplayedElement(null); @@ -419,6 +470,7 @@ function preLoadCss(cssUrl) { } return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), + // @ts-expect-error item => { const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { @@ -437,12 +489,16 @@ function preLoadCss(cssUrl) { } } + /** + * @param {HashChangeEvent|null} ev + */ function onHashChange(ev) { // If we're in mobile mode, we should hide the sidebar in any case. hideSidebar(); handleHashes(ev); } + // @ts-expect-error function openParentDetails(elem) { while (elem) { if (elem.tagName === "DETAILS") { @@ -452,18 +508,25 @@ function preLoadCss(cssUrl) { } } + // @ts-expect-error function expandSection(id) { openParentDetails(document.getElementById(id)); } + // @ts-expect-error function handleEscape(ev) { + // @ts-expect-error searchState.clearInputTimeout(); + // @ts-expect-error searchState.hideResults(); ev.preventDefault(); + // @ts-expect-error searchState.defocus(); + // @ts-expect-error window.hideAllModals(true); // true = reset focus for tooltips } + // @ts-expect-error function handleShortcut(ev) { // Don't interfere with browser shortcuts const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; @@ -471,8 +534,11 @@ function preLoadCss(cssUrl) { return; } + // @ts-expect-error if (document.activeElement.tagName === "INPUT" && + // @ts-expect-error document.activeElement.type !== "checkbox" && + // @ts-expect-error document.activeElement.type !== "radio") { switch (getVirtualKey(ev)) { case "Escape": @@ -489,6 +555,7 @@ function preLoadCss(cssUrl) { case "S": case "/": ev.preventDefault(); + // @ts-expect-error searchState.focus(); break; @@ -515,6 +582,7 @@ function preLoadCss(cssUrl) { document.addEventListener("keydown", handleShortcut); function addSidebarItems() { + // @ts-expect-error if (!window.SIDEBAR_ITEMS) { return; } @@ -529,6 +597,7 @@ function preLoadCss(cssUrl) { * "Modules", or "Macros". */ function block(shortty, id, longty) { + // @ts-expect-error const filtered = window.SIDEBAR_ITEMS[shortty]; if (!filtered) { return; @@ -564,7 +633,9 @@ function preLoadCss(cssUrl) { li.appendChild(link); ul.appendChild(li); } + // @ts-expect-error sidebar.appendChild(h3); + // @ts-expect-error sidebar.appendChild(ul); } @@ -600,6 +671,7 @@ function preLoadCss(cssUrl) { } // + // @ts-expect-error window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); @@ -615,18 +687,22 @@ function preLoadCss(cssUrl) { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). + // @ts-expect-error onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { return; } + // @ts-expect-error aliases.split(",").forEach(alias => { inlined_types.add(alias); }); }); } + // @ts-expect-error let currentNbImpls = implementors.getElementsByClassName("impl").length; + // @ts-expect-error const traitName = document.querySelector(".main-heading h1 > .trait").textContent; const baseIdName = "impl-" + traitName + "-"; const libs = Object.getOwnPropertyNames(imp); @@ -636,6 +712,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( + // @ts-expect-error (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { @@ -663,6 +740,7 @@ function preLoadCss(cssUrl) { code.innerHTML = struct[TEXT_IDX]; addClass(code, "code-header"); + // @ts-expect-error onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); @@ -681,12 +759,15 @@ function preLoadCss(cssUrl) { addClass(display, "impl"); display.appendChild(anchor); display.appendChild(code); + // @ts-expect-error list.appendChild(display); currentNbImpls += 1; } } }; + // @ts-expect-error if (window.pending_implementors) { + // @ts-expect-error window.register_implementors(window.pending_implementors); } @@ -719,12 +800,15 @@ function preLoadCss(cssUrl) { * * - After processing all of the impls, it sorts the sidebar items by name. * - * @param {{[cratename: string]: Array>}} impl + * @param {{[cratename: string]: Array>}} imp */ + // @ts-expect-error window.register_type_impls = imp => { + // @ts-expect-error if (!imp || !imp[window.currentCrate]) { return; } + // @ts-expect-error window.pending_type_impls = null; const idMap = new Map(); @@ -744,6 +828,7 @@ function preLoadCss(cssUrl) { let associatedConstants = document.querySelector(".sidebar .block.associatedconstant"); let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation"); + // @ts-expect-error for (const impList of imp[window.currentCrate]) { const types = impList.slice(2); const text = impList[0]; @@ -772,20 +857,28 @@ function preLoadCss(cssUrl) { h.appendChild(link); trait_implementations = outputList; trait_implementations_header = outputListHeader; + // @ts-expect-error sidebarSection.appendChild(h); sidebarTraitList = document.createElement("ul"); sidebarTraitList.className = "block trait-implementation"; + // @ts-expect-error sidebarSection.appendChild(sidebarTraitList); + // @ts-expect-error mainContent.appendChild(outputListHeader); + // @ts-expect-error mainContent.appendChild(outputList); } else { implementations = outputList; if (trait_implementations) { + // @ts-expect-error mainContent.insertBefore(outputListHeader, trait_implementations_header); + // @ts-expect-error mainContent.insertBefore(outputList, trait_implementations_header); } else { const mainContent = document.querySelector("#main-content"); + // @ts-expect-error mainContent.appendChild(outputListHeader); + // @ts-expect-error mainContent.appendChild(outputList); } } @@ -793,6 +886,7 @@ function preLoadCss(cssUrl) { const template = document.createElement("template"); template.innerHTML = text; + // @ts-expect-error onEachLazy(template.content.querySelectorAll("a"), elem => { const href = elem.getAttribute("href"); @@ -800,6 +894,7 @@ function preLoadCss(cssUrl) { elem.setAttribute("href", window.rootPath + href); } }); + // @ts-expect-error onEachLazy(template.content.querySelectorAll("[id]"), el => { let i = 0; if (idMap.has(el.id)) { @@ -817,6 +912,7 @@ function preLoadCss(cssUrl) { const oldHref = `#${el.id}`; const newHref = `#${el.id}-${i}`; el.id = `${el.id}-${i}`; + // @ts-expect-error onEachLazy(template.content.querySelectorAll("a[href]"), link => { if (link.getAttribute("href") === oldHref) { link.href = newHref; @@ -830,11 +926,14 @@ function preLoadCss(cssUrl) { if (isTrait) { const li = document.createElement("li"); const a = document.createElement("a"); + // @ts-expect-error a.href = `#${template.content.querySelector(".impl").id}`; a.textContent = traitName; li.appendChild(a); + // @ts-expect-error sidebarTraitList.append(li); } else { + // @ts-expect-error onEachLazy(templateAssocItems, item => { let block = hasClass(item, "associatedtype") ? associatedTypes : ( hasClass(item, "associatedconstant") ? associatedConstants : ( @@ -856,10 +955,14 @@ function preLoadCss(cssUrl) { const insertionReference = methods || sidebarTraitList; if (insertionReference) { const insertionReferenceH = insertionReference.previousElementSibling; + // @ts-expect-error sidebarSection.insertBefore(blockHeader, insertionReferenceH); + // @ts-expect-error sidebarSection.insertBefore(block, insertionReferenceH); } else { + // @ts-expect-error sidebarSection.appendChild(blockHeader); + // @ts-expect-error sidebarSection.appendChild(block); } if (hasClass(item, "associatedtype")) { @@ -896,11 +999,14 @@ function preLoadCss(cssUrl) { list.replaceChildren(...newChildren); } }; + // @ts-expect-error if (window.pending_type_impls) { + // @ts-expect-error window.register_type_impls(window.pending_type_impls); } function addSidebarCrates() { + // @ts-expect-error if (!window.ALL_CRATES) { return; } @@ -914,6 +1020,7 @@ function preLoadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block crate"; + // @ts-expect-error for (const crate of window.ALL_CRATES) { const link = document.createElement("a"); link.href = window.rootPath + crate + "/index.html"; @@ -933,17 +1040,20 @@ function preLoadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; } }); + // @ts-expect-error innerToggle.children[0].innerText = "Summary"; } function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && @@ -952,6 +1062,7 @@ function preLoadCss(cssUrl) { e.open = false; } }); + // @ts-expect-error innerToggle.children[0].innerText = "Show all"; } @@ -977,9 +1088,11 @@ function preLoadCss(cssUrl) { const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true"; const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; + // @ts-expect-error function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { + // @ts-expect-error onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); @@ -991,6 +1104,7 @@ function preLoadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; @@ -1002,6 +1116,7 @@ function preLoadCss(cssUrl) { }); }()); + // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { if (document.querySelector(".rustdoc.src")) { // We are in the source code page, nothing to be done here! @@ -1009,6 +1124,7 @@ function preLoadCss(cssUrl) { } onEachLazy(document.querySelectorAll( ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", + // @ts-expect-error ), x => { const parent = x.parentNode; const line_numbers = parent.querySelectorAll(".example-line-numbers"); @@ -1027,33 +1143,41 @@ function preLoadCss(cssUrl) { }); }; + // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { + // @ts-expect-error onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { x.parentNode.removeChild(x); }); }; if (getSettingValue("line-numbers") === "true") { + // @ts-expect-error window.rustdoc_add_line_numbers_to_examples(); } function showSidebar() { + // @ts-expect-error window.hideAllModals(false); const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error addClass(sidebar, "shown"); } function hideSidebar() { const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error removeClass(sidebar, "shown"); } window.addEventListener("resize", () => { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT) { // As a workaround to the behavior of `contains: layout` used in doc togglers, // tooltip popovers are positioned using javascript. // // This means when the window is resized, we need to redo the layout. + // @ts-expect-error const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE; const force_visible = base.TOOLTIP_FORCE_VISIBLE; hideTooltip(false); @@ -1069,6 +1193,7 @@ function preLoadCss(cssUrl) { mainElem.addEventListener("click", hideSidebar); } + // @ts-expect-error onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links ( tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes @@ -1079,7 +1204,9 @@ function preLoadCss(cssUrl) { }); }); + // @ts-expect-error onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { + // @ts-expect-error el.addEventListener("click", e => { if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { e.preventDefault(); @@ -1090,15 +1217,17 @@ function preLoadCss(cssUrl) { /** * Show a tooltip immediately. * - * @param {DOMElement} e - The tooltip's anchor point. The DOM is consulted to figure - * out what the tooltip should contain, and where it should be - * positioned. + * @param {HTMLElement} e - The tooltip's anchor point. The DOM is consulted to figure + * out what the tooltip should contain, and where it should be + * positioned. */ function showTooltip(e) { const notable_ty = e.getAttribute("data-notable-ty"); + // @ts-expect-error if (!window.NOTABLE_TRAITS && notable_ty) { const data = document.getElementById("notable-traits-data"); if (data) { + // @ts-expect-error window.NOTABLE_TRAITS = JSON.parse(data.innerText); } else { throw new Error("showTooltip() called with notable without any notable traits!"); @@ -1106,36 +1235,44 @@ function preLoadCss(cssUrl) { } // Make this function idempotent. If the tooltip is already shown, avoid doing extra work // and leave it alone. + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) { + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); return; } + // @ts-expect-error window.hideAllModals(false); const wrapper = document.createElement("div"); if (notable_ty) { wrapper.innerHTML = "
" + + // @ts-expect-error window.NOTABLE_TRAITS[notable_ty] + "
"; } else { // Replace any `title` attribute with `data-title` to avoid double tooltips. - if (e.getAttribute("title") !== null) { - e.setAttribute("data-title", e.getAttribute("title")); + const ttl = e.getAttribute("title"); + if (ttl !== null) { + e.setAttribute("data-title", ttl); e.removeAttribute("title"); } - if (e.getAttribute("data-title") !== null) { + const dttl = e.getAttribute("data-title"); + if (dttl !== null) { const titleContent = document.createElement("div"); titleContent.className = "content"; - titleContent.appendChild(document.createTextNode(e.getAttribute("data-title"))); + titleContent.appendChild(document.createTextNode(dttl)); wrapper.appendChild(titleContent); } } wrapper.className = "tooltip popover"; const focusCatcher = document.createElement("div"); focusCatcher.setAttribute("tabindex", "0"); + // @ts-expect-error focusCatcher.onfocus = hideTooltip; wrapper.appendChild(focusCatcher); const pos = e.getBoundingClientRect(); // 5px overlap so that the mouse can easily travel from place to place wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px"; + // @ts-expect-error wrapper.style.left = 0; wrapper.style.right = "auto"; wrapper.style.visibility = "hidden"; @@ -1152,8 +1289,11 @@ function preLoadCss(cssUrl) { ); } wrapper.style.visibility = ""; + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT = wrapper; + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); wrapper.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. @@ -1164,7 +1304,7 @@ function preLoadCss(cssUrl) { }; wrapper.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. - if (ev.pointerType !== "mouse") { + if (ev.pointerType !== "mouse" || !(ev.relatedTarget instanceof HTMLElement)) { return; } if (!e.TOOLTIP_FORCE_VISIBLE && !e.contains(ev.relatedTarget)) { @@ -1180,23 +1320,27 @@ function preLoadCss(cssUrl) { * was called, that timeout gets cleared. If the tooltip is already in the requested state, * this function will still clear any pending timeout, but otherwise do nothing. * - * @param {DOMElement} element - The tooltip's anchor point. The DOM is consulted to figure - * out what the tooltip should contain, and where it should be - * positioned. + * @param {HTMLElement} element - The tooltip's anchor point. The DOM is consulted to figure + * out what the tooltip should contain, and where it should be + * positioned. * @param {boolean} show - If true, the tooltip will be made visible. If false, it will * be hidden. */ function setTooltipHoverTimeout(element, show) { clearTooltipHoverTimeout(element); + // @ts-expect-error if (!show && !window.CURRENT_TOOLTIP_ELEMENT) { // To "hide" an already hidden element, just cancel its timeout. return; } + // @ts-expect-error if (show && window.CURRENT_TOOLTIP_ELEMENT) { // To "show" an already visible element, just cancel its timeout. return; } + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE !== element) { // Don't do anything if another tooltip is already visible. return; @@ -1214,22 +1358,29 @@ function preLoadCss(cssUrl) { * If a show/hide timeout was set by `setTooltipHoverTimeout`, cancel it. If none exists, * do nothing. * - * @param {DOMElement} element - The tooltip's anchor point, - * as passed to `setTooltipHoverTimeout`. + * @param {HTMLElement} element - The tooltip's anchor point, + * as passed to `setTooltipHoverTimeout`. */ function clearTooltipHoverTimeout(element) { if (element.TOOLTIP_HOVER_TIMEOUT !== undefined) { + // @ts-expect-error removeClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out"); clearTimeout(element.TOOLTIP_HOVER_TIMEOUT); delete element.TOOLTIP_HOVER_TIMEOUT; } } + // @ts-expect-error function tooltipBlurHandler(event) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget) ) { // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari. @@ -1251,32 +1402,45 @@ function preLoadCss(cssUrl) { * If set to `false`, leave keyboard focus alone. */ function hideTooltip(focus) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) { if (focus) { + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus(); } + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; } + // @ts-expect-error document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT = null; } } + // @ts-expect-error onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = () => { e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) { hideTooltip(true); } else { showTooltip(e); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.focus(); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; } return false; }; + // @ts-expect-error e.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { @@ -1284,6 +1448,7 @@ function preLoadCss(cssUrl) { } setTooltipHoverTimeout(e, true); }; + // @ts-expect-error e.onpointermove = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { @@ -1291,12 +1456,15 @@ function preLoadCss(cssUrl) { } setTooltipHoverTimeout(e, true); }; + // @ts-expect-error e.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } + // @ts-expect-error if (!e.TOOLTIP_FORCE_VISIBLE && window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)) { // Tooltip pointer leave gesture: // @@ -1329,6 +1497,7 @@ function preLoadCss(cssUrl) { // * https://www.nngroup.com/articles/tooltip-guidelines/ // * https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown setTooltipHoverTimeout(e, false); + // @ts-expect-error addClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out"); } }; @@ -1338,6 +1507,7 @@ function preLoadCss(cssUrl) { if (sidebar_menu_toggle) { sidebar_menu_toggle.addEventListener("click", () => { const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error if (!hasClass(sidebar, "shown")) { showSidebar(); } else { @@ -1346,12 +1516,18 @@ function preLoadCss(cssUrl) { }); } + // @ts-expect-error function helpBlurHandler(event) { + // @ts-expect-error if (!getHelpButton().contains(document.activeElement) && + // @ts-expect-error !getHelpButton().contains(event.relatedTarget) && + // @ts-expect-error !getSettingsButton().contains(document.activeElement) && + // @ts-expect-error !getSettingsButton().contains(event.relatedTarget) ) { + // @ts-expect-error window.hidePopoverMenus(); } } @@ -1427,14 +1603,18 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (isHelpPage) { const help_section = document.createElement("section"); help_section.appendChild(container); + // @ts-expect-error document.getElementById("main-content").appendChild(help_section); container.style.display = "block"; } else { const help_button = getHelpButton(); + // @ts-expect-error help_button.appendChild(container); container.onblur = helpBlurHandler; + // @ts-expect-error help_button.onblur = helpBlurHandler; + // @ts-expect-error help_button.children[0].onblur = helpBlurHandler; } @@ -1446,8 +1626,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * Pass "true" to reset focus for tooltip popovers. */ + // @ts-expect-error window.hideAllModals = switchFocus => { hideSidebar(); + // @ts-expect-error window.hidePopoverMenus(); hideTooltip(switchFocus); }; @@ -1455,7 +1637,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm /** * Hide all the popover menus. */ + // @ts-expect-error window.hidePopoverMenus = () => { + // @ts-expect-error onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); @@ -1474,10 +1658,12 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * @return {HTMLElement} */ function getHelpMenu(buildNeeded) { + // @ts-expect-error let menu = getHelpButton().querySelector(".popover"); if (!menu && buildNeeded) { menu = buildHelpMenu(); } + // @ts-expect-error return menu; } @@ -1489,9 +1675,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // other modals. const button = getHelpButton(); addClass(button, "help-open"); + // @ts-expect-error button.querySelector("a").focus(); const menu = getHelpMenu(true); if (menu.style.display === "none") { + // @ts-expect-error window.hideAllModals(); menu.style.display = ""; } @@ -1506,8 +1694,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // If user clicks with a moderator, though, use default browser behavior, // probably opening in a new window or tab. if (!helpLink.contains(helpLink) || + // @ts-expect-error event.ctrlKey || + // @ts-expect-error event.altKey || + // @ts-expect-error event.metaKey) { return; } @@ -1517,6 +1708,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (shouldShowHelp) { showHelp(); } else { + // @ts-expect-error window.hidePopoverMenus(); } }); @@ -1527,6 +1719,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm addSidebarCrates(); onHashChange(null); window.addEventListener("hashchange", onHashChange); + // @ts-expect-error searchState.setup(); }()); @@ -1580,6 +1773,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm removeClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "false"); if (document.querySelector(".rustdoc.src")) { + // @ts-expect-error window.rustdocToggleSrcSidebar(); } e.preventDefault(); @@ -1589,6 +1783,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Pointer capture. // // Resizing is a single-pointer gesture. Any secondary pointer is ignored + // @ts-expect-error let currentPointerId = null; // "Desired" sidebar size. @@ -1596,6 +1791,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // This is stashed here for window resizing. If the sidebar gets // shrunk to maintain BODY_MIN, and then the user grows the window again, // it gets the sidebar to restore its size. + // @ts-expect-error let desiredSidebarSize = null; // Sidebar resize debouncer. @@ -1626,7 +1822,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // through that size when using the shrink-to-nothing gesture. function hideSidebar() { if (isSrcPage) { + // @ts-expect-error window.rustdocCloseSourceSidebar(); + // @ts-expect-error updateLocalStorage("src-sidebar-width", null); // [RUSTDOCIMPL] CSS variable fast path // @@ -1639,14 +1837,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // // So, to clear it, we need to clear all three. document.documentElement.style.removeProperty("--src-sidebar-width"); + // @ts-expect-error sidebar.style.removeProperty("--src-sidebar-width"); + // @ts-expect-error resizer.style.removeProperty("--src-sidebar-width"); } else { addClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "true"); + // @ts-expect-error updateLocalStorage("desktop-sidebar-width", null); document.documentElement.style.removeProperty("--desktop-sidebar-width"); + // @ts-expect-error sidebar.style.removeProperty("--desktop-sidebar-width"); + // @ts-expect-error resizer.style.removeProperty("--desktop-sidebar-width"); } } @@ -1659,6 +1862,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // remains visible all the time on there. function showSidebar() { if (isSrcPage) { + // @ts-expect-error window.rustdocShowSourceSidebar(); } else { removeClass(document.documentElement, "hide-sidebar"); @@ -1674,6 +1878,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm */ function changeSidebarSize(size) { if (isSrcPage) { + // @ts-expect-error updateLocalStorage("src-sidebar-width", size); // [RUSTDOCIMPL] CSS variable fast path // @@ -1681,11 +1886,16 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // because the sidebar isn't actually loaded yet, // we scope this update to the sidebar to avoid hitting a slow // path in WebKit. + // @ts-expect-error sidebar.style.setProperty("--src-sidebar-width", size + "px"); + // @ts-expect-error resizer.style.setProperty("--src-sidebar-width", size + "px"); } else { + // @ts-expect-error updateLocalStorage("desktop-sidebar-width", size); + // @ts-expect-error sidebar.style.setProperty("--desktop-sidebar-width", size + "px"); + // @ts-expect-error resizer.style.setProperty("--desktop-sidebar-width", size + "px"); } } @@ -1701,7 +1911,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Respond to the resize handle event. // This function enforces size constraints, and implements the // shrink-to-nothing gesture based on thresholds defined above. + // @ts-expect-error function resize(e) { + // @ts-expect-error if (currentPointerId === null || currentPointerId !== e.pointerId) { return; } @@ -1719,15 +1931,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm changeSidebarSize(constrainedPos); desiredSidebarSize = constrainedPos; if (pendingSidebarResizingFrame !== false) { + // @ts-expect-error clearTimeout(pendingSidebarResizingFrame); } + // @ts-expect-error pendingSidebarResizingFrame = setTimeout(() => { + // @ts-expect-error if (currentPointerId === null || pendingSidebarResizingFrame === false) { return; } pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", + // @ts-expect-error desiredSidebarSize + "px", ); }, 100); @@ -1739,51 +1955,69 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } stopResize(); + // @ts-expect-error if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { changeSidebarSize(window.innerWidth - BODY_MIN); + // @ts-expect-error } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) { + // @ts-expect-error changeSidebarSize(desiredSidebarSize); } }); + // @ts-expect-error function stopResize(e) { + // @ts-expect-error if (currentPointerId === null) { return; } if (e) { e.preventDefault(); } + // @ts-expect-error desiredSidebarSize = sidebar.getBoundingClientRect().width; + // @ts-expect-error removeClass(resizer, "active"); window.removeEventListener("pointermove", resize, false); window.removeEventListener("pointerup", stopResize, false); removeClass(document.documentElement, "sidebar-resizing"); document.documentElement.style.removeProperty( "--resizing-sidebar-width"); + // @ts-expect-error if (resizer.releasePointerCapture) { + // @ts-expect-error resizer.releasePointerCapture(currentPointerId); currentPointerId = null; } } + // @ts-expect-error function initResize(e) { + // @ts-expect-error if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } + // @ts-expect-error if (resizer.setPointerCapture) { + // @ts-expect-error resizer.setPointerCapture(e.pointerId); + // @ts-expect-error if (!resizer.hasPointerCapture(e.pointerId)) { // unable to capture pointer; something else has it // on iOS, this usually means you long-clicked a link instead + // @ts-expect-error resizer.releasePointerCapture(e.pointerId); return; } currentPointerId = e.pointerId; } + // @ts-expect-error window.hideAllModals(false); e.preventDefault(); window.addEventListener("pointermove", resize, false); window.addEventListener("pointercancel", stopResize, false); window.addEventListener("pointerup", stopResize, false); + // @ts-expect-error addClass(resizer, "active"); addClass(document.documentElement, "sidebar-resizing"); + // @ts-expect-error const pos = e.clientX - sidebar.offsetLeft - 3; document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px"); desiredSidebarSize = null; @@ -1795,6 +2029,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // and the copy buttons on the code examples. (function() { // Common functions to copy buttons. + // @ts-expect-error function copyContentToClipboard(content) { const el = document.createElement("textarea"); el.value = content; @@ -1809,6 +2044,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm document.body.removeChild(el); } + // @ts-expect-error function copyButtonAnimation(button) { button.classList.add("clicked"); @@ -1831,6 +2067,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Most page titles are ' in - Rust', except // modules (which don't have the first part) and keywords/primitives // (which don't have a module path) + // @ts-expect-error const title = document.querySelector("title").textContent.replace(" - Rust", ""); const [item, module] = title.split(" in "); const path = [item]; @@ -1843,6 +2080,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }; // Copy buttons on code examples. + // @ts-expect-error function copyCode(codeElem) { if (!codeElem) { // Should never happen, but the world is a dark and dangerous place. @@ -1851,6 +2089,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyContentToClipboard(codeElem.textContent); } + // @ts-expect-error function getExampleWrap(event) { let elem = event.target; while (!hasClass(elem, "example-wrap")) { @@ -1866,6 +2105,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return elem; } + // @ts-expect-error function addCopyButton(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -1896,9 +2136,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } const scrapedWrapped = elem.parentElement; + // @ts-expect-error window.updateScrapedExample(scrapedWrapped, parent); } + // @ts-expect-error function showHideCodeExampleButtons(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -1917,6 +2159,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm buttons.classList.toggle("keep-visible"); } + // @ts-expect-error onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => { elem.addEventListener("mouseover", addCopyButton); elem.addEventListener("click", showHideCodeExampleButtons); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts new file mode 100644 index 000000000000..18a3e22113b8 --- /dev/null +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -0,0 +1,387 @@ +// This file contains type definitions that are processed by the TypeScript Compiler but are +// not put into the JavaScript we include as part of the documentation. It is used for +// type checking. See README.md in this directory for more info. + +/* eslint-disable */ +declare global { + interface Window { + /** Make the current theme easy to find */ + currentTheme: HTMLLinkElement|null; + /** Used by the popover tooltip code. */ + RUSTDOC_TOOLTIP_HOVER_MS: number; + /** Used by the popover tooltip code. */ + RUSTDOC_TOOLTIP_HOVER_EXIT_MS: number; + /** Search engine data used by main.js and search.js */ + searchState: rustdoc.SearchState; + /** Global option, with a long list of "../"'s */ + rootPath: string|null; + /** + * Currently opened crate. + * As a multi-page application, we know this never changes once set. + */ + currentCrate: string|null; + } + interface HTMLElement { + /** Used by the popover tooltip code. */ + TOOLTIP_FORCE_VISIBLE: boolean|undefined, + /** Used by the popover tooltip code */ + TOOLTIP_HOVER_TIMEOUT: Timeout|undefined, + } +} + +export = rustdoc; + +declare namespace rustdoc { + interface SearchState { + rustdocToolbar: HTMLElement|null; + loadingText: string; + input: HTMLInputElement|null; + title: string; + titleBeforeSearch: string; + timeout: number|null; + currentTab: number; + focusedByTab: [number|null, number|null, number|null]; + clearInputTimeout: function; + outputElement: function(): HTMLElement|null; + focus: function(); + defocus: function(); + showResults: function(HTMLElement|null|undefined); + removeQueryParameters: function(); + hideResults: function(); + getQueryStringParams: function(): Object.; + origPlaceholder: string; + setup: function(); + setLoadingSearch: function(); + descShards: Map; + loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise; + loadedDescShard: function(string, number, string); + isDisplayed: function(): boolean, + } + + interface SearchDescShard { + crate: string; + promise: Promise|null; + resolve: function(string[])|null; + shard: number; + } + + /** + * A single parsed "atom" in a search query. For example, + * + * std::fmt::Formatter, Write -> Result<()> + * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ + * ┃ │ ┗ QueryElement { ┊ + * ┃ │ name: Result ┊ + * ┃ │ generics: [ ┊ + * ┃ │ QueryElement ┘ + * ┃ │ name: () + * ┃ │ ] + * ┃ │ } + * ┃ └ QueryElement { + * ┃ name: Write + * ┃ } + * ┗ QueryElement { + * name: Formatter + * pathWithoutLast: std::fmt + * } + */ + interface QueryElement { + name: string, + id: number|null, + fullPath: Array, + pathWithoutLast: Array, + pathLast: string, + normalizedPathLast: string, + generics: Array, + bindings: Map>, + typeFilter: number|null, + } + + /** + * Same as QueryElement, but bindings and typeFilter support strings + */ + interface ParserQueryElement { + name: string, + id: number|null, + fullPath: Array, + pathWithoutLast: Array, + pathLast: string, + normalizedPathLast: string, + generics: Array, + bindings: Map>, + bindingName: {name: string, generics: ParserQueryElement[]}|null, + typeFilter: string|null, + } + + /** + * Intermediate parser state. Discarded when parsing is done. + */ + interface ParserState { + pos: number; + length: number; + totalElems: number; + genericsElems: number; + typeFilter: (null|string); + userQuery: string; + isInBinding: (null|{name: string, generics: ParserQueryElement[]}); + } + + /** + * A complete parsed query. + */ + interface ParsedQuery { + userQuery: string, + elems: Array, + returned: Array, + foundElems: number, + totalElems: number, + literalSearch: boolean, + hasReturnArrow: boolean, + correction: string|null, + proposeCorrectionFrom: string|null, + proposeCorrectionTo: string|null, + typeFingerprint: Uint32Array, + error: Array | null, + } + + /** + * An entry in the search index database. + */ + interface Row { + crate: string, + descShard: SearchDescShard, + id: number, + name: string, + normalizedName: string, + word: string, + parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), + path: string, + ty: number, + type?: FunctionSearchType + } + + /** + * The viewmodel for the search engine results page. + */ + interface ResultsTable { + in_args: Array, + returned: Array, + others: Array, + query: ParsedQuery, + } + + type Results = Map; + + /** + * An annotated `Row`, used in the viewmodel. + */ + interface ResultObject { + desc: string, + displayPath: string, + fullPath: string, + href: string, + id: number, + dist: number, + path_dist: number, + name: string, + normalizedName: string, + word: string, + index: number, + parent: (Object|undefined), + path: string, + ty: number, + type?: FunctionSearchType, + paramNames?: string[], + displayType: Promise>>|null, + displayTypeMappedNames: Promise]>>|null, + item: Row, + dontValidate?: boolean, + } + + /** + * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. + * The JavaScript deserializes this into FunctionSearchType. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * An input or output can be encoded as just a number if there is only one of them, AND + * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had + * a function with a single output, and that output had a single generic: + * + * fn something() -> Result + * + * If output was allowed to be any RawFunctionType, it would look like thi + * + * [[], [50, [3, 3]]] + * + * The problem is that the above output could be interpreted as either a type with ID 50 and two + * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second + * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing + * in favor of the pair of types interpretation. This is why the `(number|Array)` + * is used instead of `(RawFunctionType|Array)`. + * + * The output can be skipped if it's actually unit and there's no type constraints. If thi + * function accepts constrained generics, then the output will be unconditionally emitted, and + * after it will come a list of trait constraints. The position of the item in the list will + * determine which type parameter it is. For example: + * + * [1, 2, 3, 4, 5] + * ^ ^ ^ ^ ^ + * | | | | - generic parameter (-3) of trait 5 + * | | | - generic parameter (-2) of trait 4 + * | | - generic parameter (-1) of trait 3 + * | - this function returns a single value (type 2) + * - this function takes a single input parameter (type 1) + * + * Or, for a less contrived version: + * + * [[[4, -1], 3], [[5, -1]], 11] + * -^^^^^^^---- ^^^^^^^ ^^ + * | | | - generic parameter, roughly `where -1: 11` + * | | | since -1 is the type parameter and 11 the trait + * | | - function output 5<-1> + * | - the overall function signature is something like + * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` + * - function input, corresponds roughly to 4<-1> + * 4 is an index into the `p` array for a type + * -1 is the generic parameter, given by 11 + * + * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like + * function inputs and outputs: + * + * [-1, -1, [4, 3]] + * ^^^^^^ where -1: 4 + 3 + * + * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array + * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in + * favor of `4 + 3`: + * + * [-1, -1, [[4, 3]]] + * ^^^^^^^^ where -1: 4 + 3 + * + * [-1, -1, [5, [4, 3]]] + * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 + * + * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i + * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. + */ + type RawFunctionSearchType = + 0 | + [(number|Array)] | + [(number|Array), (number|Array)] | + Array<(number|Array)> + ; + + /** + * A single function input or output type. This is either a single path ID, or a pair of + * [path ID, generics]. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + */ + type RawFunctionType = number | [number, Array]; + + /** + * The type signature entry in the decoded search index. + * (The "Raw" objects are encoded differently to save space in the JSON). + */ + interface FunctionSearchType { + inputs: Array, + output: Array, + where_clause: Array>, + } + + /** + * A decoded function type, made from real objects. + * `ty` will be negative for generics, positive for types, and 0 for placeholders. + */ + interface FunctionType { + id: null|number, + ty: number|null, + name?: string, + path: string|null, + exactPath: string|null, + unboxFlag: boolean, + generics: Array, + bindings: Map>, + }; + + interface HighlightedFunctionType extends FunctionType { + generics: HighlightedFunctionType[], + bindings: Map, + highlighted?: boolean; + } + + interface FingerprintableType { + id: number|null; + generics: FingerprintableType[]; + bindings: Map; + }; + + /** + * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` + * are arrays with the same length. `q`, `a`, and `c` use a sparse + * representation for compactness. + * + * `n[i]` contains the name of an item. + * + * `t[i]` contains the type of that item + * (as a string of characters that represent an offset in `itemTypes`). + * + * `d[i]` contains the description of that item. + * + * `q` contains the full paths of the items. For compactness, it is a set of + * (index, path) pairs used to create a map. If a given index `i` is + * not present, this indicates "same as the last index present". + * + * `i[i]` contains an item's parent, usually a module. For compactness, + * it is a set of indexes into the `p` array. + * + * `f` contains function signatures, or `0` if the item isn't a function. + * More information on how they're encoded can be found in rustc-dev-guide + * + * Functions are themselves encoded as arrays. The first item is a list of + * types representing the function's inputs, and the second list item is a list + * of types representing the function's output. Tuples are flattened. + * Types are also represented as arrays; the first item is an index into the `p` + * array, while the second is a list of types representing any generic parameters. + * + * b[i] contains an item's impl disambiguator. This is only present if an item + * is defined in an impl block and, the impl block's type has more than one associated + * item with the same name. + * + * `a` defines aliases with an Array of pairs: [name, offset], where `offset` + * points into the n/t/d/q/i/f arrays. + * + * `doc` contains the description of the crate. + * + * `p` is a list of path/type pairs. It is used for parents and function parameters. + * The first item is the type, the second is the name, the third is the visible path (if any) and + * the fourth is the canonical path used for deduplication (if any). + * + * `r` is the canonical path used for deduplication of re-exported items. + * It is not used for associated items like methods (that's the fourth element + * of `p`) but is used for modules items like free functions. + * + * `c` is an array of item indices that are deprecated. + */ + type RawSearchIndexCrate = { + doc: string, + a: Object, + n: Array, + t: string, + D: string, + e: string, + q: Array<[number, string]>, + i: string, + f: string, + p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, + b: Array<[number, String]>, + c: string, + r: Array<[number, number]>, + P: Array<[number, string]>, + }; + + type VlqData = VlqData[] | number; +} diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 98c53b8656f5..d08f15a5bfa8 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,5 +1,8 @@ /* global addClass, hasClass, removeClass, onEachLazy */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 660484c133c3..1ad32721e068 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -10,11 +10,19 @@ if (!Array.prototype.toSpliced) { // Can't use arrow functions, because we want `this` Array.prototype.toSpliced = function() { const me = this.slice(); + // @ts-expect-error Array.prototype.splice.apply(me, arguments); return me; }; } +/** + * + * @template T + * @param {Iterable} arr + * @param {function(T): any} func + * @param {function(T): boolean} funcBtwn + */ function onEachBtwn(arr, func, funcBtwn) { let skipped = true; for (const value of arr) { @@ -98,9 +106,24 @@ const NO_TYPE_FILTER = -1; * documentation. */ const editDistanceState = { + /** + * @type {number[]} + */ current: [], + /** + * @type {number[]} + */ prev: [], + /** + * @type {number[]} + */ prevPrev: [], + /** + * @param {string} a + * @param {string} b + * @param {number} limit + * @returns + */ calculate: function calculate(a, b, limit) { // Ensure that `b` is the shorter string, minimizing memory use. if (a.length < b.length) { @@ -186,14 +209,28 @@ const editDistanceState = { }, }; +/** + * @param {string} a + * @param {string} b + * @param {number} limit + * @returns + */ function editDistance(a, b, limit) { return editDistanceState.calculate(a, b, limit); } +/** + * @param {string} c + * @returns {boolean} + */ function isEndCharacter(c) { return "=,>-])".indexOf(c) !== -1; } +/** + * @param {number} ty + * @returns + */ function isFnLikeTy(ty) { return ty === TY_FN || ty === TY_METHOD || ty === TY_TYMETHOD; } @@ -212,7 +249,7 @@ function isSeparatorCharacter(c) { /** * Returns `true` if the current parser position is starting with "->". * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -223,7 +260,7 @@ function isReturnArrow(parserState) { /** * Increase current parser position until it doesn't find a whitespace anymore. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState */ function skipWhitespace(parserState) { while (parserState.pos < parserState.userQuery.length) { @@ -238,7 +275,7 @@ function skipWhitespace(parserState) { /** * Returns `true` if the previous character is `lookingFor`. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * @param {String} lookingFor * * @return {boolean} @@ -260,8 +297,8 @@ function prevIs(parserState, lookingFor) { /** * Returns `true` if the last element in the `elems` argument has generics. * - * @param {Array} elems - * @param {ParserState} parserState + * @param {Array} elems + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -270,6 +307,13 @@ function isLastElemGeneric(elems, parserState) { prevIs(parserState, ">"); } +/** + * + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {rustdoc.ParserQueryElement[]} elems + * @param {boolean} isInGenerics + */ function getFilteredNextElem(query, parserState, elems, isInGenerics) { const start = parserState.pos; if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { @@ -294,6 +338,8 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = elems.pop(); checkExtraTypeFilterCharacters(start, parserState); + // typeFilterElem is not null. If it was, the elems.length check would have fired. + // @ts-expect-error parserState.typeFilter = typeFilterElem.normalizedPathLast; parserState.pos += 1; parserState.totalElems -= 1; @@ -309,12 +355,13 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { * If there is no `endChar`, this function will implicitly stop at the end * without raising an error. * - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. - * @param {string} endChar - This function will stop when it'll encounter this - * character. - * @returns {{foundSeparator: bool}} + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {Array} elems + * - This is where the new {QueryElement} will be added. + * @param {string} endChar - This function will stop when it'll encounter this + * character. + * @returns {{foundSeparator: boolean}} */ function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; @@ -385,6 +432,7 @@ function getItemsBefore(query, parserState, elems, endChar) { throw ["Unexpected ", c, " after ", extra]; } if (!foundStopChar) { + /** @type {string[]} */ let extra = []; if (isLastElemGeneric(query.elems, parserState)) { extra = [" after ", ">"]; @@ -463,12 +511,14 @@ function getItemsBefore(query, parserState, elems, endChar) { } /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {Array} elems + * - This is where the new {QueryElement} will be added. * @param {boolean} isInGenerics */ function getNextElem(query, parserState, elems, isInGenerics) { + /** @type {rustdoc.ParserQueryElement[]} */ const generics = []; skipWhitespace(parserState); @@ -588,6 +638,7 @@ function getNextElem(query, parserState, elems, isInGenerics) { getFilteredNextElem(query, parserState, generics, isInGenerics); generics[generics.length - 1].bindingName = makePrimitiveElement("output"); } else { + // @ts-expect-error generics.push(makePrimitiveElement(null, { bindingName: makePrimitiveElement("output"), typeFilter: null, @@ -640,7 +691,8 @@ function getNextElem(query, parserState, elems, isInGenerics) { * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored * if empty). * - * @param {ParserState} parserState + * @param {number} start + * @param {rustdoc.ParserState} parserState */ function checkExtraTypeFilterCharacters(start, parserState) { const query = parserState.userQuery.slice(start, parserState.pos).trim(); @@ -658,12 +710,13 @@ function checkExtraTypeFilterCharacters(start, parserState) { } /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {string} name - Name of the query element. - * @param {Array} generics - List of generics of this query element. + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {string} name - Name of the query element. + * @param {Array} generics - List of generics of this query element. + * @param {boolean} isInGenerics * - * @return {QueryElement} - The newly created `QueryElement`. + * @return {rustdoc.ParserQueryElement} - The newly created `QueryElement`. */ function createQueryElement(query, parserState, name, generics, isInGenerics) { const path = name.trim(); @@ -756,9 +809,15 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { }; } +/** + * + * @param {string} name + * @param {Object=} extra + * @returns {rustdoc.ParserQueryElement} + */ function makePrimitiveElement(name, extra) { return Object.assign({ - name, + name: name, id: null, fullPath: [name], pathWithoutLast: [], @@ -781,8 +840,8 @@ function makePrimitiveElement(name, extra) { * * There is more than one element. * * There is no closing `"`. * - * @param {ParsedQuery} query - * @param {ParserState} parserState + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState * @param {boolean} isInGenerics */ function getStringElem(query, parserState, isInGenerics) { @@ -813,9 +872,9 @@ function getStringElem(query, parserState, isInGenerics) { * character or the end of the query. It returns the position of the last * character of the ident. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * - * @return {integer} + * @return {number} */ function getIdentEndPosition(parserState) { let afterIdent = consumeIdent(parserState); @@ -890,6 +949,10 @@ function getIdentEndPosition(parserState) { return end; } +/** + * @param {string} c + * @returns + */ function isSpecialStartCharacter(c) { return "<\"".indexOf(c) !== -1; } @@ -897,7 +960,7 @@ function isSpecialStartCharacter(c) { /** * Returns `true` if the current parser position is starting with "::". * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -909,7 +972,7 @@ function isPathStart(parserState) { * If the current parser position is at the beginning of an identifier, * move the position to the end of it and return `true`. Otherwise, return `false`. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -935,14 +998,25 @@ function isPathSeparator(c) { return c === ":" || c === " "; } +/** + * @template T + */ class VlqHexDecoder { + /** + * @param {string} string + * @param {function(rustdoc.VlqData): T} cons + */ constructor(string, cons) { this.string = string; this.cons = cons; this.offset = 0; + /** @type {T[]} */ this.backrefQueue = []; } - // call after consuming `{` + /** + * call after consuming `{` + * @returns {rustdoc.VlqData[]} + */ decodeList() { let c = this.string.charCodeAt(this.offset); const ret = []; @@ -953,7 +1027,10 @@ class VlqHexDecoder { this.offset += 1; // eat cb return ret; } - // consumes and returns a list or integer + /** + * consumes and returns a list or integer + * @returns {rustdoc.VlqData} + */ decode() { let n = 0; let c = this.string.charCodeAt(this.offset); @@ -972,6 +1049,9 @@ class VlqHexDecoder { this.offset += 1; return sign ? -value : value; } + /** + * @returns {T} + */ next() { const c = this.string.charCodeAt(this.offset); // sixteen characters after "0" are backref @@ -994,6 +1074,7 @@ class VlqHexDecoder { } } class RoaringBitmap { + /** @param {string} str */ constructor(str) { // https://github.com/RoaringBitmap/RoaringFormatSpec // @@ -1063,6 +1144,7 @@ class RoaringBitmap { } } } + /** @param {number} keyvalue */ contains(keyvalue) { const key = keyvalue >> 16; const value = keyvalue & 0xFFFF; @@ -1091,10 +1173,15 @@ class RoaringBitmap { } class RoaringBitmapRun { + /** + * @param {number} runcount + * @param {Uint8Array} array + */ constructor(runcount, array) { this.runcount = runcount; this.array = array; } + /** @param {number} value */ contains(value) { // Binary search algorithm copied from // https://en.wikipedia.org/wiki/Binary_search#Procedure @@ -1120,10 +1207,15 @@ class RoaringBitmapRun { } } class RoaringBitmapArray { + /** + * @param {number} cardinality + * @param {Uint8Array} array + */ constructor(cardinality, array) { this.cardinality = cardinality; this.array = array; } + /** @param {number} value */ contains(value) { // Binary search algorithm copied from // https://en.wikipedia.org/wiki/Binary_search#Procedure @@ -1148,9 +1240,13 @@ class RoaringBitmapArray { } } class RoaringBitmapBits { + /** + * @param {Uint8Array} array + */ constructor(array) { this.array = array; } + /** @param {number} value */ contains(value) { return !!(this.array[value >> 3] & (1 << (value & 7))); } @@ -1176,9 +1272,9 @@ class RoaringBitmapBits { * matches * : A list of search index IDs for this node. * - * @typedef {{ - * children: [NameTrie], - * matches: [number], + * @type {{ + * children: NameTrie[], + * matches: number[], * }} */ class NameTrie { @@ -1186,9 +1282,20 @@ class NameTrie { this.children = []; this.matches = []; } + /** + * @param {string} name + * @param {number} id + * @param {Map} tailTable + */ insert(name, id, tailTable) { this.insertSubstring(name, 0, id, tailTable); } + /** + * @param {string} name + * @param {number} substart + * @param {number} id + * @param {Map} tailTable + */ insertSubstring(name, substart, id, tailTable) { const l = name.length; if (substart === l) { @@ -1201,10 +1308,13 @@ class NameTrie { } else { child = new NameTrie(); this.children[sb] = child; + /** @type {NameTrie[]} */ let sste; if (substart >= 2) { const tail = name.substring(substart - 2, substart + 1); if (tailTable.has(tail)) { + // it's not undefined + // @ts-expect-error sste = tailTable.get(tail); } else { sste = []; @@ -1216,6 +1326,10 @@ class NameTrie { child.insertSubstring(name, substart + 1, id, tailTable); } } + /** + * @param {string} name + * @param {Map} tailTable + */ search(name, tailTable) { const results = new Set(); this.searchSubstringPrefix(name, 0, results); @@ -1226,6 +1340,8 @@ class NameTrie { this.searchLev(name, 0, levParams, results); const tail = name.substring(0, 3); if (tailTable.has(tail)) { + // it's not undefined + // @ts-expect-error for (const entry of tailTable.get(tail)) { entry.searchSubstringPrefix(name, 3, results); } @@ -1233,6 +1349,11 @@ class NameTrie { } return [...results]; } + /** + * @param {string} name + * @param {number} substart + * @param {Set} results + */ searchSubstringPrefix(name, substart, results) { const l = name.length; if (substart === l) { @@ -1240,14 +1361,18 @@ class NameTrie { results.add(match); } // breadth-first traversal orders prefix matches by length + /** @type {NameTrie[]} */ let unprocessedChildren = []; for (const child of this.children) { if (child) { unprocessedChildren.push(child); } } + /** @type {NameTrie[]} */ let nextSet = []; while (unprocessedChildren.length !== 0) { + /** @type {NameTrie} */ + // @ts-expect-error const next = unprocessedChildren.pop(); for (const child of next.children) { if (child) { @@ -1270,10 +1395,18 @@ class NameTrie { } } } + /** + * @param {string} name + * @param {number} substart + * @param {Lev2TParametricDescription|Lev1TParametricDescription} levParams + * @param {Set} results + */ searchLev(name, substart, levParams, results) { const stack = [[this, 0]]; const n = levParams.n; while (stack.length !== 0) { + // It's not empty + //@ts-expect-error const [trie, levState] = stack.pop(); for (const [charCode, child] of trie.children.entries()) { if (!child) { @@ -1305,6 +1438,11 @@ class NameTrie { } class DocSearch { + /** + * @param {Map} rawSearchIndex + * @param {string} rootPath + * @param {rustdoc.SearchState} searchState + */ constructor(rawSearchIndex, rootPath, searchState) { /** * @type {Map} @@ -1317,19 +1455,19 @@ class DocSearch { /** * @type {Uint32Array} */ - this.functionTypeFingerprint = null; + this.functionTypeFingerprint = new Uint32Array(0); /** * Map from normalized type names to integers. Used to make type search * more efficient. * - * @type {Map} + * @type {Map} */ this.typeNameIdMap = new Map(); /** * Map from type ID to associated type name. Used for display, * not for search. * - * @type {Map} + * @type {Map} */ this.assocTypeIdNameMap = new Map(); this.ALIASES = new Map(); @@ -1338,64 +1476,88 @@ class DocSearch { /** * Special type name IDs for searching by array. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfArray = this.buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); /** * Special type name IDs for searching `fn`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); /** * Special type name IDs for searching `fnmut`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); /** * Special type name IDs for searching `fnonce`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); /** * Special type name IDs for searching higher order functions (`->` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfHof = this.buildTypeMapIndex("->"); /** * Special type name IDs the output assoc type. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); /** * Special type name IDs for searching by reference. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Map>} + * @type {Map>} */ this.EMPTY_BINDINGS_MAP = new Map(); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Array} + * @type {Array} */ this.EMPTY_GENERICS_ARRAY = []; @@ -1403,7 +1565,7 @@ class DocSearch { * Object pool for function types with no bindings or generics. * This is reset after loading the index. * - * @type {Map} + * @type {Map} */ this.TYPES_POOL = new Map(); @@ -1422,8 +1584,9 @@ class DocSearch { this.tailTable = new Map(); /** - * @type {Array} + * @type {Array} */ + // @ts-expect-error this.searchIndex = this.buildIndex(rawSearchIndex); } @@ -1436,9 +1599,9 @@ class DocSearch { * get the same ID. * * @param {string} name - * @param {boolean} isAssocType - True if this is an assoc type + * @param {boolean=} isAssocType - True if this is an assoc type * - * @returns {integer} + * @returns {number?} */ buildTypeMapIndex(name, isAssocType) { if (name === "" || name === null) { @@ -1446,12 +1609,14 @@ class DocSearch { } if (this.typeNameIdMap.has(name)) { + /** @type {{id: number, assocOnly: boolean}} */ + // @ts-expect-error const obj = this.typeNameIdMap.get(name); - obj.assocOnly = isAssocType && obj.assocOnly; + obj.assocOnly = !!(isAssocType && obj.assocOnly); return obj.id; } else { const id = this.typeNameIdMap.size; - this.typeNameIdMap.set(name, { id, assocOnly: isAssocType }); + this.typeNameIdMap.set(name, { id, assocOnly: !!isAssocType }); return id; } } @@ -1469,13 +1634,26 @@ class DocSearch { * The format for individual function types is encoded in * librustdoc/html/render/mod.rs: impl Serialize for RenderType * - * @param {null|Array} types - * @param {Array<{name: string, ty: number}>} lowercasePaths + * @param {null|Array} types + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean, + * }>} lowercasePaths * - * @return {Array} + * @return {Array} */ buildItemSearchTypeAll(types, paths, lowercasePaths) { - return types.length > 0 ? + return types && types.length > 0 ? types.map(type => this.buildItemSearchType(type, paths, lowercasePaths)) : this.EMPTY_GENERICS_ARRAY; } @@ -1483,7 +1661,22 @@ class DocSearch { /** * Converts a single type. * - * @param {RawFunctionType} type + * @param {rustdoc.RawFunctionType} type + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean, + * }>} lowercasePaths + * @param {boolean=} isAssocType */ buildItemSearchType(type, paths, lowercasePaths, isAssocType) { const PATH_INDEX_DATA = 0; @@ -1501,7 +1694,9 @@ class DocSearch { paths, lowercasePaths, ); + // @ts-expect-error if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { + // @ts-expect-error bindings = new Map(type[BINDINGS_DATA].map(binding => { const [assocType, constraints] = binding; // Associated type constructors are represented sloppily in rustdoc's @@ -1524,7 +1719,7 @@ class DocSearch { } } /** - * @type {FunctionType} + * @type {rustdoc.FunctionType} */ let result; if (pathIndex < 0) { @@ -1555,7 +1750,7 @@ class DocSearch { } else { const item = lowercasePaths[pathIndex - 1]; const id = this.buildTypeMapIndex(item.name, isAssocType); - if (isAssocType) { + if (isAssocType && id !== null) { this.assocTypeIdNameMap.set(id, paths[pathIndex - 1].name); } result = { @@ -1585,6 +1780,7 @@ class DocSearch { if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { let ok = true; for (const [k, v] of cr.bindings.entries()) { + // @ts-expect-error const v2 = result.bindings.get(v); if (!v2) { ok = false; @@ -1629,7 +1825,7 @@ class DocSearch { * [^1]: Distance is the relatively naive metric of counting the number of distinct items in * the function that are not present in the query. * - * @param {FunctionType|QueryElement} type - a single type + * @param {rustdoc.FingerprintableType} type - a single type * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits */ buildFunctionTypeFingerprint(type, output) { @@ -1647,9 +1843,12 @@ class DocSearch { input === this.typeNameIdOfFnOnce) { input = this.typeNameIdOfHof; } - // http://burtleburtle.net/bob/hash/integer.html - // ~~ is toInt32. It's used before adding, so - // the number stays in safe integer range. + /** + * http://burtleburtle.net/bob/hash/integer.html + * ~~ is toInt32. It's used before adding, so + * the number stays in safe integer range. + * @param {number} k + */ const hashint1 = k => { k = (~~k + 0x7ed55d16) + (k << 12); k = (k ^ 0xc761c23c) ^ (k >>> 19); @@ -1658,6 +1857,7 @@ class DocSearch { k = (~~k + 0xfd7046c5) + (k << 3); return (k ^ 0xb55a4f09) ^ (k >>> 16); }; + /** @param {number} k */ const hashint2 = k => { k = ~k + (k << 15); k ^= k >>> 12; @@ -1684,6 +1884,14 @@ class DocSearch { for (const g of type.generics) { this.buildFunctionTypeFingerprint(g, output); } + /** + * @type {{ + * id: number|null, + * ty: number, + * generics: rustdoc.FingerprintableType[], + * bindings: Map + * }} + */ const fb = { id: null, ty: 0, @@ -1700,7 +1908,7 @@ class DocSearch { /** * Convert raw search index into in-memory search index. * - * @param {[string, RawSearchIndexCrate][]} rawSearchIndex + * @param {Map} rawSearchIndex */ buildIndex(rawSearchIndex) { /** @@ -1714,19 +1922,37 @@ class DocSearch { * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {Array<{name: string, ty: number}>} paths - * @param {Array<{name: string, ty: number}>} lowercasePaths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} lowercasePaths * - * @return {null|FunctionSearchType} + * @return {function(rustdoc.RawFunctionSearchType): null|rustdoc.FunctionSearchType} */ const buildFunctionSearchTypeCallback = (paths, lowercasePaths) => { - return functionSearchType => { + /** + * @param {rustdoc.RawFunctionSearchType} functionSearchType + */ + const cb = functionSearchType => { if (functionSearchType === 0) { return null; } const INPUTS_DATA = 0; const OUTPUT_DATA = 1; - let inputs, output; + /** @type {rustdoc.FunctionType[]} */ + let inputs; + /** @type {rustdoc.FunctionType[]} */ + let output; if (typeof functionSearchType[INPUTS_DATA] === "number") { inputs = [ this.buildItemSearchType( @@ -1753,6 +1979,7 @@ class DocSearch { ]; } else { output = this.buildItemSearchTypeAll( + // @ts-expect-error functionSearchType[OUTPUT_DATA], paths, lowercasePaths, @@ -1765,8 +1992,10 @@ class DocSearch { const l = functionSearchType.length; for (let i = 2; i < l; ++i) { where_clause.push(typeof functionSearchType[i] === "number" + // @ts-expect-error ? [this.buildItemSearchType(functionSearchType[i], paths, lowercasePaths)] : this.buildItemSearchTypeAll( + // @ts-expect-error functionSearchType[i], paths, lowercasePaths, @@ -1776,6 +2005,7 @@ class DocSearch { inputs, output, where_clause, }; }; + return cb; }; const searchIndex = []; @@ -1798,7 +2028,12 @@ class DocSearch { for (const [crate, crateCorpus] of rawSearchIndex) { // a string representing the lengths of each description shard // a string representing the list of function types - const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => { + /** @type {number} */ + // @ts-expect-error + const n = noop; + return n; + }); let descShard = { crate, shard: 0, @@ -1817,7 +2052,7 @@ class DocSearch { /** * List of generic function type parameter names. * Used for display, not for searching. - * @type {[string]} + * @type {string[]} */ let lastParamNames = []; @@ -1847,6 +2082,8 @@ class DocSearch { id += 1; searchIndex.push(crateRow); currentIndex += 1; + // it's not undefined + // @ts-expect-error if (!this.searchIndexEmptyDesc.get(crate).contains(0)) { descIndex += 1; } @@ -1870,7 +2107,7 @@ class DocSearch { const implDisambiguator = new Map(crateCorpus.b); // an array of [(Number) item type, // (String) name] - const paths = crateCorpus.p; + const rawPaths = crateCorpus.p; // an array of [(String) alias name // [Number] index to items] const aliases = crateCorpus.a; @@ -1879,33 +2116,61 @@ class DocSearch { // an item whose index is not present will fall back to the previous present path const itemParamNames = new Map(crateCorpus.P); - // an array of [{name: String, ty: Number}] + /** + * @type {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} + */ const lowercasePaths = []; + /** + * @type {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} + */ + const paths = []; // a string representing the list of function types const itemFunctionDecoder = new VlqHexDecoder( crateCorpus.f, + // @ts-expect-error buildFunctionSearchTypeCallback(paths, lowercasePaths), ); // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode - let len = paths.length; + let len = rawPaths.length; let lastPath = itemPaths.get(0); for (let i = 0; i < len; ++i) { - const elem = paths[i]; + const elem = rawPaths[i]; const ty = elem[0]; const name = elem[1]; let path = null; if (elem.length > 2 && elem[2] !== null) { + // @ts-expect-error path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; lastPath = path; } - const exactPath = elem.length > 3 && elem[3] !== null ? + let exactPath = elem.length > 3 && elem[3] !== null ? + // @ts-expect-error itemPaths.get(elem[3]) : path; const unboxFlag = elem.length > 4 && !!elem[4]; + if (path === undefined) { + path = null; + } + if (exactPath === undefined) { + exactPath = null; + } + lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); paths[i] = { ty, name, path, exactPath, unboxFlag }; } @@ -1924,6 +2189,7 @@ class DocSearch { for (let i = 0; i < len; ++i) { const bitIndex = i + 1; if (descIndex >= descShard.len && + // @ts-expect-error !this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descShard = { crate, @@ -1938,8 +2204,11 @@ class DocSearch { } const name = itemNames[i] === "" ? lastName : itemNames[i]; const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); + /** @type {string} */ + // @ts-expect-error const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; const paramNames = itemParamNames.has(i) ? + // @ts-expect-error itemParamNames.get(i).split(",") : lastParamNames; const type = itemFunctionDecoder.next(); @@ -1971,7 +2240,9 @@ class DocSearch { descShard, descIndex, exactPath: itemReexports.has(i) ? + // @ts-expect-error itemPaths.get(itemReexports.get(i)) : path, + // @ts-expect-error parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, type, paramNames, @@ -1987,6 +2258,7 @@ class DocSearch { searchIndex.push(row); lastPath = row.path; lastParamNames = row.paramNames; + // @ts-expect-error if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descIndex += 1; } @@ -2002,13 +2274,16 @@ class DocSearch { continue; } + // @ts-expect-error let currentNameAliases; if (currentCrateAliases.has(alias_name)) { currentNameAliases = currentCrateAliases.get(alias_name); } else { currentNameAliases = []; + // @ts-expect-error currentCrateAliases.set(alias_name, currentNameAliases); } + // @ts-expect-error for (const local_alias of aliases[alias_name]) { currentNameAliases.push(local_alias + currentIndex); } @@ -2030,11 +2305,15 @@ class DocSearch { * * When adding new things to the parser, add them there, too! * - * @param {string} val - The user query + * @param {string} userQuery - The user query * - * @return {ParsedQuery} - The parsed query + * @return {rustdoc.ParsedQuery} - The parsed query */ static parseQuery(userQuery) { + /** + * @param {string} typename + * @returns {number} + */ function itemTypeFromName(typename) { const index = itemTypes.findIndex(i => i === typename); if (index < 0) { @@ -2043,14 +2322,19 @@ class DocSearch { return index; } + /** + * @param {rustdoc.ParserQueryElement} elem + */ function convertTypeFilterOnElem(elem) { if (elem.typeFilter !== null) { let typeFilter = elem.typeFilter; if (typeFilter === "const") { typeFilter = "constant"; } + // @ts-expect-error elem.typeFilter = itemTypeFromName(typeFilter); } else { + // @ts-expect-error elem.typeFilter = NO_TYPE_FILTER; } for (const elem2 of elem.generics) { @@ -2068,7 +2352,7 @@ class DocSearch { * * @param {string} userQuery * - * @return {ParsedQuery} + * @return {rustdoc.ParsedQuery} */ function newParsedQuery(userQuery) { return { @@ -2094,8 +2378,8 @@ class DocSearch { * Parses the provided `query` input to fill `parserState`. If it encounters an error while * parsing `query`, it'll throw an error. * - * @param {ParsedQuery} query - * @param {ParserState} parserState + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState */ function parseInput(query, parserState) { let foundStopChar = true; @@ -2125,6 +2409,7 @@ class DocSearch { if (!foundStopChar) { let extra = ""; if (isLastElemGeneric(query.elems, parserState)) { + // @ts-expect-error extra = [" after ", ">"]; } else if (prevIs(parserState, "\"")) { throw ["Cannot have more than one element if you use quotes"]; @@ -2208,6 +2493,8 @@ class DocSearch { } } catch (err) { query = newParsedQuery(userQuery); + // is string list + // @ts-expect-error query.error = err; return query; } @@ -2224,25 +2511,167 @@ class DocSearch { /** * Executes the parsed query and builds a {ResultsTable}. * - * @param {ParsedQuery} parsedQuery - The parsed user query - * @param {Object} [filterCrates] - Crate to search in if defined - * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher + * @param {rustdoc.ParsedQuery} origParsedQuery + * - The parsed user query + * @param {Object} [filterCrates] - Crate to search in if defined + * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher * - * @return {ResultsTable} + * @return {Promise} */ - async execQuery(parsedQuery, filterCrates, currentCrate) { + async execQuery(origParsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); + /** @type {rustdoc.ParsedQuery} */ + // @ts-expect-error + const parsedQuery = origParsedQuery; + + const queryLen = + parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + + parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); + const maxEditDistance = Math.floor(queryLen / 3); + + /** + * @type {Map} + */ + const genericSymbols = new Map(); + + /** + * Convert names to ids in parsed query elements. + * This is not used for the "In Names" tab, but is used for the + * "In Params", "In Returns", and "In Function Signature" tabs. + * + * If there is no matching item, but a close-enough match, this + * function also that correction. + * + * See `buildTypeMapIndex` for more information. + * + * @param {rustdoc.QueryElement} elem + * @param {boolean} isAssocType + */ + const convertNameToId = (elem, isAssocType) => { + const loweredName = elem.pathLast.toLowerCase(); + if (this.typeNameIdMap.has(loweredName) && + // @ts-expect-error + (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { + // @ts-expect-error + elem.id = this.typeNameIdMap.get(loweredName).id; + } else if (!parsedQuery.literalSearch) { + let match = null; + let matchDist = maxEditDistance + 1; + let matchName = ""; + for (const [name, { id, assocOnly }] of this.typeNameIdMap) { + const dist = Math.min( + editDistance(name, loweredName, maxEditDistance), + editDistance(name, elem.normalizedPathLast, maxEditDistance), + ); + if (dist <= matchDist && dist <= maxEditDistance && + (isAssocType || !assocOnly)) { + if (dist === matchDist && matchName > name) { + continue; + } + match = id; + matchDist = dist; + matchName = name; + } + } + if (match !== null) { + parsedQuery.correction = matchName; + } + elem.id = match; + } + if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 + && elem.generics.length === 0 && elem.bindings.size === 0) + || elem.typeFilter === TY_GENERIC) { + if (genericSymbols.has(elem.normalizedPathLast)) { + // @ts-expect-error + elem.id = genericSymbols.get(elem.normalizedPathLast); + } else { + elem.id = -(genericSymbols.size + 1); + genericSymbols.set(elem.normalizedPathLast, elem.id); + } + if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { + // Silly heuristic to catch if the user probably meant + // to not write a generic parameter. We don't use it, + // just bring it up. + const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); + let matchDist = maxPartDistance + 1; + let matchName = ""; + for (const name of this.typeNameIdMap.keys()) { + const dist = editDistance( + name, + elem.normalizedPathLast, + maxPartDistance, + ); + if (dist <= matchDist && dist <= maxPartDistance) { + if (dist === matchDist && matchName > name) { + continue; + } + matchDist = dist; + matchName = name; + } + } + if (matchName !== "") { + parsedQuery.proposeCorrectionFrom = elem.name; + parsedQuery.proposeCorrectionTo = matchName; + } + } + elem.typeFilter = TY_GENERIC; + } + if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { + // Rust does not have HKT + parsedQuery.error = [ + "Generic type parameter ", + elem.name, + " does not accept generic parameters", + ]; + } + for (const elem2 of elem.generics) { + // @ts-expect-error + convertNameToId(elem2); + } + elem.bindings = new Map(Array.from(elem.bindings.entries()) + .map(entry => { + const [name, constraints] = entry; + // @ts-expect-error + if (!this.typeNameIdMap.has(name)) { + parsedQuery.error = [ + "Type parameter ", + // @ts-expect-error + name, + " does not exist", + ]; + return [0, []]; + } + for (const elem2 of constraints) { + convertNameToId(elem2, false); + } + + // @ts-expect-error + return [this.typeNameIdMap.get(name).id, constraints]; + }), + ); + }; + + for (const elem of parsedQuery.elems) { + convertNameToId(elem, false); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); + } + for (const elem of parsedQuery.returned) { + convertNameToId(elem, false); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); + } + + /** * Creates the query results. * - * @param {Array} results_in_args - * @param {Array} results_returned - * @param {Array} results_others - * @param {ParsedQuery} parsedQuery + * @param {Array} results_in_args + * @param {Array} results_returned + * @param {Array} results_others + * @param {rustdoc.ParsedQuery} parsedQuery * - * @return {ResultsTable} + * @return {rustdoc.ResultsTable} */ function createQueryResults( results_in_args, @@ -2257,6 +2686,7 @@ class DocSearch { }; } + // @ts-expect-error const buildHrefAndPath = item => { let displayPath; let href; @@ -2320,6 +2750,7 @@ class DocSearch { return [displayPath, href, `${exactPath}::${name}`]; }; + // @ts-expect-error function pathSplitter(path) { const tmp = "" + path.replace(/::/g, "::"); if (tmp.endsWith("")) { @@ -2332,10 +2763,9 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[ResultObject]} results + * @param {[rustdoc.ResultObject]} results * @param {"sig"|"elems"|"returned"|null} typeInfo - * @param {ParsedQuery} query - * @returns {[ResultObject]} + * @returns {[rustdoc.ResultObject]} */ const transformResults = (results, typeInfo) => { const duplicates = new Set(); @@ -2350,7 +2780,9 @@ class DocSearch { }, this.searchIndex[result.id]); // To be sure than it some items aren't considered as duplicate. + // @ts-expect-error obj.fullPath = res[2] + "|" + obj.ty; + // @ts-expect-error if (duplicates.has(obj.fullPath)) { continue; } @@ -2363,14 +2795,18 @@ class DocSearch { if (duplicates.has(res[2] + "|" + TY_IMPORT)) { continue; } + // @ts-expect-error duplicates.add(obj.fullPath); duplicates.add(res[2]); if (typeInfo !== null) { + // @ts-expect-error obj.displayTypeSignature = + // @ts-expect-error this.formatDisplayTypeSignature(obj, typeInfo); } + // @ts-expect-error obj.href = res[1]; out.push(obj); if (out.length >= MAX_RESULTS) { @@ -2378,6 +2814,7 @@ class DocSearch { } } } + // @ts-expect-error return out; }; @@ -2388,30 +2825,34 @@ class DocSearch { * The output is formatted as an array of hunks, where odd numbered * hunks are highlighted and even numbered ones are not. * - * @param {ResultObject} obj + * @param {rustdoc.ResultObject} obj * @param {"sig"|"elems"|"returned"|null} typeInfo - * @param {ParsedQuery} query - * @returns Promise< + * @returns {Promise<{ * "type": Array, * "mappedNames": Map, * "whereClause": Map>, - * > + * }>} */ this.formatDisplayTypeSignature = async(obj, typeInfo) => { + const objType = obj.type; + if (!objType) { + return {type: [], mappedNames: new Map(), whereClause: new Map()}; + } let fnInputs = null; let fnOutput = null; + // @ts-expect-error let mgens = null; if (typeInfo !== "elems" && typeInfo !== "returned") { fnInputs = unifyFunctionTypes( - obj.type.inputs, + objType.inputs, parsedQuery.elems, - obj.type.where_clause, + objType.where_clause, null, mgensScratch => { fnOutput = unifyFunctionTypes( - obj.type.output, + objType.output, parsedQuery.returned, - obj.type.where_clause, + objType.where_clause, mgensScratch, mgensOut => { mgens = mgensOut; @@ -2424,11 +2865,11 @@ class DocSearch { 0, ); } else { - const arr = typeInfo === "elems" ? obj.type.inputs : obj.type.output; + const arr = typeInfo === "elems" ? objType.inputs : objType.output; const highlighted = unifyFunctionTypes( arr, parsedQuery.elems, - obj.type.where_clause, + objType.where_clause, null, mgensOut => { mgens = mgensOut; @@ -2443,15 +2884,16 @@ class DocSearch { } } if (!fnInputs) { - fnInputs = obj.type.inputs; + fnInputs = objType.inputs; } if (!fnOutput) { - fnOutput = obj.type.output; + fnOutput = objType.output; } const mappedNames = new Map(); const whereClause = new Map(); - const fnParamNames = obj.paramNames; + const fnParamNames = obj.paramNames || []; + // @ts-expect-error const queryParamNames = []; /** * Recursively writes a map of IDs to query generic names, @@ -2461,10 +2903,10 @@ class DocSearch { * mapping `(-1, "X")`, and the writeFn function looks up the entry * for -1 to form the final, user-visible mapping of "X is T". * - * @param {QueryElement} queryElem + * @param {rustdoc.QueryElement} queryElem */ const remapQuery = queryElem => { - if (queryElem.id < 0) { + if (queryElem.id !== null && queryElem.id < 0) { queryParamNames[-1 - queryElem.id] = queryElem.name; } if (queryElem.generics.length > 0) { @@ -2483,7 +2925,7 @@ class DocSearch { * Index 0 is not highlighted, index 1 is highlighted, * index 2 is not highlighted, etc. * - * @param {{name: string, highlighted: bool|undefined}} fnType - input + * @param {{name?: string, highlighted?: boolean}} fnType - input * @param {[string]} result */ const pushText = (fnType, result) => { @@ -2496,6 +2938,7 @@ class DocSearch { // needs coerced to a boolean. if (!!(result.length % 2) === !!fnType.highlighted) { result.push(""); + // @ts-expect-error } else if (result.length === 0 && !!fnType.highlighted) { result.push(""); result.push(""); @@ -2508,7 +2951,7 @@ class DocSearch { * Write a higher order function type: either a function pointer * or a trait bound on Fn, FnMut, or FnOnce. * - * @param {FunctionType} fnType - input + * @param {rustdoc.HighlightedFunctionType} fnType - input * @param {[string]} result */ const writeHof = (fnType, result) => { @@ -2548,7 +2991,7 @@ class DocSearch { * Write a primitive type with special syntax, like `!` or `[T]`. * Returns `false` if the supplied type isn't special. * - * @param {FunctionType} fnType + * @param {rustdoc.HighlightedFunctionType} fnType * @param {[string]} result */ const writeSpecialPrimitive = (fnType, result) => { @@ -2563,6 +3006,7 @@ class DocSearch { onEachBtwn( fnType.generics, nested => writeFn(nested, result), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); pushText({ name: sb, highlighted: fnType.highlighted }, result); @@ -2573,9 +3017,10 @@ class DocSearch { onEachBtwn( fnType.generics, value => { - prevHighlighted = value.highlighted; + prevHighlighted = !!value.highlighted; writeFn(value, result); }, + // @ts-expect-error value => pushText({ name: " ", highlighted: prevHighlighted && value.highlighted, @@ -2593,25 +3038,27 @@ class DocSearch { * like slices, with their own formatting. It also handles * updating the where clause and generic type param map. * - * @param {FunctionType} fnType + * @param {rustdoc.HighlightedFunctionType} fnType * @param {[string]} result */ const writeFn = (fnType, result) => { - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { if (fnParamNames[-1 - fnType.id] === "") { // Normally, there's no need to shown an unhighlighted // where clause, but if it's impl Trait, then we do. const generics = fnType.generics.length > 0 ? fnType.generics : - obj.type.where_clause[-1 - fnType.id]; + objType.where_clause[-1 - fnType.id]; for (const nested of generics) { writeFn(nested, result); } return; + // @ts-expect-error } else if (mgens) { for (const [queryId, fnId] of mgens) { if (fnId === fnType.id) { mappedNames.set( + // @ts-expect-error queryParamNames[-1 - queryId], fnParamNames[-1 - fnType.id], ); @@ -2622,13 +3069,17 @@ class DocSearch { name: fnParamNames[-1 - fnType.id], highlighted: !!fnType.highlighted, }, result); + // @ts-expect-error const where = []; onEachBtwn( fnType.generics, + // @ts-expect-error nested => writeFn(nested, where), + // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, where), ); if (where.length > 0) { + // @ts-expect-error whereClause.set(fnParamNames[-1 - fnType.id], where); } } else { @@ -2650,12 +3101,15 @@ class DocSearch { fnType.bindings, ([key, values]) => { const name = this.assocTypeIdNameMap.get(key); + // @ts-expect-error if (values.length === 1 && values[0].id < 0 && + // @ts-expect-error `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id]) { // the internal `Item=Iterator::Item` type variable should be // shown in the where clause and name mapping output, but is // redundant in this spot for (const value of values) { + // @ts-expect-error writeFn(value, []); } return true; @@ -2672,12 +3126,14 @@ class DocSearch { onEachBtwn( values || [], value => writeFn(value, result), + // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, result), ); if (values.length !== 1) { pushText({ name: ")", highlighted: false }, result); } }, + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); } @@ -2687,6 +3143,7 @@ class DocSearch { onEachBtwn( fnType.generics, value => writeFn(value, result), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); if (hasBindings || fnType.generics.length > 0) { @@ -2694,19 +3151,26 @@ class DocSearch { } } }; + // @ts-expect-error const type = []; onEachBtwn( fnInputs, + // @ts-expect-error fnType => writeFn(fnType, type), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); + // @ts-expect-error pushText({ name: " -> ", highlighted: false }, type); onEachBtwn( fnOutput, + // @ts-expect-error fnType => writeFn(fnType, type), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); + // @ts-expect-error return {type, mappedNames, whereClause}; }; @@ -2714,10 +3178,10 @@ class DocSearch { * This function takes a result map, and sorts it by various criteria, including edit * distance, substring match, and the crate it comes from. * - * @param {Results} results - * @param {boolean} isType + * @param {rustdoc.Results} results + * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {string} preferredCrate - * @returns {Promise<[ResultObject]>} + * @returns {Promise<[rustdoc.ResultObject]>} */ const sortResults = async(results, typeInfo, preferredCrate) => { const userQuery = parsedQuery.userQuery; @@ -2733,12 +3197,12 @@ class DocSearch { // we are doing a return-type based search, // deprioritize "clone-like" results, // ie. functions that also take the queried type as an argument. - const hasType = result.item && result.item.type; - if (!hasType) { + const resultItemType = result.item && result.item.type; + if (!resultItemType) { continue; } - const inputs = result.item.type.inputs; - const where_clause = result.item.type.where_clause; + const inputs = resultItemType.inputs; + const where_clause = resultItemType.where_clause; if (containsTypeFromQuery(inputs, where_clause)) { result.path_dist *= 100; result.dist *= 100; @@ -2748,35 +3212,38 @@ class DocSearch { } result_list.sort((aaa, bbb) => { - let a, b; + /** @type {number} */ + let a; + /** @type {number} */ + let b; // sort by exact case-sensitive match if (isMixedCase) { - a = (aaa.item.name !== userQuery); - b = (bbb.item.name !== userQuery); + a = Number(aaa.item.name !== userQuery); + b = Number(bbb.item.name !== userQuery); if (a !== b) { return a - b; } } // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== normalizedUserQuery); - b = (bbb.word !== normalizedUserQuery); + a = Number(aaa.word !== normalizedUserQuery); + b = Number(bbb.word !== normalizedUserQuery); if (a !== b) { return a - b; } // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); + a = Number(aaa.index < 0); + b = Number(bbb.index < 0); if (a !== b) { return a - b; } // in type based search, put functions first if (parsedQuery.hasReturnArrow) { - a = !isFnLikeTy(aaa.item.ty); - b = !isFnLikeTy(bbb.item.ty); + a = Number(!isFnLikeTy(aaa.item.ty)); + b = Number(!isFnLikeTy(bbb.item.ty)); if (a !== b) { return a - b; } @@ -2784,80 +3251,93 @@ class DocSearch { // Sort by distance in the path part, if specified // (less changes required to match means higher rankings) - a = aaa.path_dist; - b = bbb.path_dist; + a = Number(aaa.path_dist); + b = Number(bbb.path_dist); if (a !== b) { return a - b; } // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; + a = Number(aaa.index); + b = Number(bbb.index); if (a !== b) { return a - b; } // Sort by distance in the name part, the last part of the path // (less changes required to match means higher rankings) - a = (aaa.dist); - b = (bbb.dist); + a = Number(aaa.dist); + b = Number(bbb.dist); if (a !== b) { return a - b; } // sort deprecated items later - a = this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); + a = Number( + // @ts-expect-error + this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); if (a !== b) { return a - b; } // sort by crate (current crate comes first) - a = (aaa.item.crate !== preferredCrate); - b = (bbb.item.crate !== preferredCrate); + a = Number(aaa.item.crate !== preferredCrate); + b = Number(bbb.item.crate !== preferredCrate); if (a !== b) { return a - b; } // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; + a = Number(aaa.word.length); + b = Number(bbb.word.length); if (a !== b) { return a - b; } // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { - return (a > b ? +1 : -1); + let aw = aaa.word; + let bw = bbb.word; + if (aw !== bw) { + return (aw > bw ? +1 : -1); } // sort by description (no description goes later) - a = this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); + a = Number( + // @ts-expect-error + this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); if (a !== b) { return a - b; } // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; + a = Number(aaa.item.ty); + b = Number(bbb.item.ty); if (a !== b) { return a - b; } // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { - return (a > b ? +1 : -1); + aw = aaa.item.path; + bw = bbb.item.path; + if (aw !== bw) { + return (aw > bw ? +1 : -1); } // que sera, sera return 0; }); + // @ts-expect-error return transformResults(result_list, typeInfo); }; @@ -2871,17 +3351,19 @@ class DocSearch { * then this function will try with a different solution, or bail with null if it * runs out of candidates. * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} fnTypesIn - The objects to check. + * @param {rustdoc.QueryElement[]} queryElems - The elements from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn * - Map query generics to function generics (never modified). - * @param {Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {function(Map?): boolean} solutionCb + * - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {[FunctionType]|null} - Returns highlighted results if a match, null otherwise. + * @return {rustdoc.HighlightedFunctionType[]|null} + * - Returns highlighted results if a match, null otherwise. */ function unifyFunctionTypes( fnTypesIn, @@ -2895,7 +3377,7 @@ class DocSearch { return null; } /** - * @type Map|null + * @type {Map|null} */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { @@ -2915,7 +3397,11 @@ class DocSearch { if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && + fnType.id < 0 && + queryElem.id !== null && + queryElem.id < 0 + ) { if (mgens && mgens.has(queryElem.id) && mgens.get(queryElem.id) !== fnType.id) { continue; @@ -2959,8 +3445,10 @@ class DocSearch { )) { continue; } + // @ts-expect-error if (fnType.id < 0) { const highlightedGenerics = unifyFunctionTypes( + // @ts-expect-error whereClause[(-fnType.id) - 1], queryElems, whereClause, @@ -2998,12 +3486,13 @@ class DocSearch { } } } + // @ts-expect-error return false; } // Multiple element recursive case /** - * @type Array + * @type {Array} */ const fnTypes = fnTypesIn.slice(); /** @@ -3033,7 +3522,7 @@ class DocSearch { continue; } let mgensScratch; - if (fnType.id < 0) { + if (fnType.id !== null && queryElem.id !== null && fnType.id < 0) { mgensScratch = new Map(mgens); if (mgensScratch.has(queryElem.id) && mgensScratch.get(queryElem.id) !== fnType.id) { @@ -3052,8 +3541,11 @@ class DocSearch { if (!queryElemsTmp) { queryElemsTmp = queryElems.slice(0, qlast); } + /** @type {rustdoc.HighlightedFunctionType[]|null} */ let unifiedGenerics = []; + // @ts-expect-error let unifiedGenericsMgens = null; + /** @type {rustdoc.HighlightedFunctionType[]|null} */ const passesUnification = unifyFunctionTypes( fnTypes, queryElemsTmp, @@ -3102,11 +3594,14 @@ class DocSearch { bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { return [k, queryElem.bindings.has(k) ? unifyFunctionTypes( v, + // @ts-expect-error queryElem.bindings.get(k), whereClause, + // @ts-expect-error unifiedGenericsMgens, solutionCb, unboxingDepth, + // @ts-expect-error ) : unifiedGenerics.splice(0, v.length)]; })), }); @@ -3128,7 +3623,7 @@ class DocSearch { )) { continue; } - const generics = fnType.id < 0 ? + const generics = fnType.id !== null && fnType.id < 0 ? whereClause[(-fnType.id) - 1] : fnType.generics; const bindings = fnType.bindings ? @@ -3171,17 +3666,19 @@ class DocSearch { * `Vec` of `Allocators` and not the implicit `Allocator` parameter that every * `Vec` has. * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Array} fnTypesIn - The objects to check. + * @param {Array} queryElems - The elements from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn * - Map functions generics to query generics (never modified). - * @param {Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {function(Map): boolean} solutionCb + * - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {[FunctionType]|null} - Returns highlighted results if a match, null otherwise. + * @return {rustdoc.HighlightedFunctionType[]|null} + * - Returns highlighted results if a match, null otherwise. */ function unifyGenericTypes( fnTypesIn, @@ -3195,10 +3692,11 @@ class DocSearch { return null; } /** - * @type Map|null + * @type {Map|null} */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { + // @ts-expect-error return solutionCb(mgens) ? fnTypesIn : null; } if (!fnTypesIn || fnTypesIn.length === 0) { @@ -3207,7 +3705,11 @@ class DocSearch { const fnType = fnTypesIn[0]; const queryElem = queryElems[0]; if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && + fnType.id < 0 && + queryElem.id !== null && + queryElem.id < 0 + ) { if (!mgens || !mgens.has(queryElem.id) || mgens.get(queryElem.id) === fnType.id ) { @@ -3238,6 +3740,7 @@ class DocSearch { queryElems.slice(1), whereClause, mgens, + // @ts-expect-error mgensScratch => { const solution = unifyFunctionTypeCheckBindings( fnType, @@ -3285,7 +3788,7 @@ class DocSearch { unboxingDepth + 1, )) { let highlightedRemaining; - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { // Where clause corresponds to `F: A + B` // ^^^^^ // The order of the constraints doesn't matter, so @@ -3295,6 +3798,7 @@ class DocSearch { [queryElem], whereClause, mgens, + // @ts-expect-error mgensScratch => { const hl = unifyGenericTypes( fnTypesIn.slice(1), @@ -3316,6 +3820,7 @@ class DocSearch { highlighted: true, }, fnType, { generics: highlightedGenerics, + // @ts-expect-error }), ...highlightedRemaining]; } } else { @@ -3327,6 +3832,7 @@ class DocSearch { [queryElem], whereClause, mgens, + // @ts-expect-error mgensScratch => { const hl = unifyGenericTypes( fnTypesIn.slice(1), @@ -3349,6 +3855,7 @@ class DocSearch { bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { return [k, highlightedGenerics.splice(0, v.length)]; })), + // @ts-expect-error }), ...highlightedRemaining]; } } @@ -3364,8 +3871,8 @@ class DocSearch { * or associated type bindings: that's not load-bearing, but it prevents unnecessary * backtracking later. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem * @param {Map|null} mgensIn - Map query generics to function generics. * @returns {boolean} */ @@ -3377,7 +3884,7 @@ class DocSearch { // fnType.id < 0 means generic // queryElem.id < 0 does too // mgensIn[queryElem.id] = fnType.id - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && fnType.id < 0 && queryElem.id !== null && queryElem.id < 0) { if ( mgensIn && mgensIn.has(queryElem.id) && mgensIn.get(queryElem.id) !== fnType.id @@ -3456,13 +3963,15 @@ class DocSearch { * ID of u32 in it, and the rest of the matching engine acts as if `Iterator` were * the type instead. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map} mgensIn - Map query generics to function generics. + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensIn - Map query generics to function generics. * Never modified. * @param {number} unboxingDepth - * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} + * @returns {false|{ + * mgens: [Map|null], simplifiedGenerics: rustdoc.FunctionType[] + * }} */ function unifyFunctionTypeCheckBindings( fnType, @@ -3486,8 +3995,10 @@ class DocSearch { } const fnTypeBindings = fnType.bindings.get(name); mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { + // @ts-expect-error const newSolutions = []; unifyFunctionTypes( + // @ts-expect-error fnTypeBindings, constraints, whereClause, @@ -3500,6 +4011,7 @@ class DocSearch { }, unboxingDepth, ); + // @ts-expect-error return newSolutions; }); } @@ -3519,14 +4031,15 @@ class DocSearch { } else { simplifiedGenerics = binds; } + // @ts-expect-error return { simplifiedGenerics, mgens: mgensSolutionSet }; } return { simplifiedGenerics, mgens: [mgensIn] }; } /** - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * @param {number} unboxingDepth * @returns {boolean} @@ -3541,7 +4054,7 @@ class DocSearch { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { if (!whereClause) { return false; } @@ -3577,14 +4090,14 @@ class DocSearch { * This function checks if the given list contains any * (non-generic) types mentioned in the query. * - * @param {Array} list - A list of function types. - * @param {[FunctionType]} where_clause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} list - A list of function types. + * @param {rustdoc.FunctionType[][]} where_clause - Trait bounds for generic items. */ function containsTypeFromQuery(list, where_clause) { if (!list) return false; for (const ty of parsedQuery.returned) { // negative type ids are generics - if (ty.id < 0) { + if (ty.id !== null && ty.id < 0) { continue; } if (checkIfInList(list, ty, where_clause, null, 0)) { @@ -3592,7 +4105,7 @@ class DocSearch { } } for (const ty of parsedQuery.elems) { - if (ty.id < 0) { + if (ty.id !== null && ty.id < 0) { continue; } if (checkIfInList(list, ty, where_clause, null, 0)) { @@ -3606,9 +4119,9 @@ class DocSearch { * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * - * @param {Array} list - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} list + * @param {rustdoc.QueryElement} elem - The element from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. * @param {number} unboxingDepth * @@ -3627,18 +4140,20 @@ class DocSearch { * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * - * @param {Row} row - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType} row + * @param {rustdoc.QueryElement} elem - The element from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * * @return {boolean} - Returns true if the type matches, false otherwise. */ + // @ts-expect-error const checkType = (row, elem, whereClause, mgens, unboxingDepth) => { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && + if (row.id !== null && elem.id !== null && + row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && row.generics.length === 0 && elem.generics.length === 0 && row.bindings.size === 0 && elem.bindings.size === 0 && // special case @@ -3648,6 +4163,7 @@ class DocSearch { ) { return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); } else { + // @ts-expect-error return unifyFunctionTypes( [row], [elem], @@ -3662,6 +4178,7 @@ class DocSearch { /** * Check a query solution for conflicting generics. */ + // @ts-expect-error const checkTypeMgensForConflict = mgens => { if (!mgens) { return true; @@ -3679,7 +4196,7 @@ class DocSearch { /** * Compute an "edit distance" that ignores missing path elements. * @param {string[]} contains search query path - * @param {Row} ty indexed item + * @param {rustdoc.Row} ty indexed item * @returns {null|number} edit distance */ function checkPath(contains, ty) { @@ -3720,6 +4237,7 @@ class DocSearch { return ret_dist > maxPathEditDistance ? null : ret_dist; } + // @ts-expect-error function typePassesFilter(filter, type) { // No filter or Exact mach if (filter <= NO_TYPE_FILTER || filter === type) return true; @@ -3741,6 +4259,7 @@ class DocSearch { return false; } + // @ts-expect-error function createAliasFromItem(item) { return { crate: item.crate, @@ -3758,11 +4277,14 @@ class DocSearch { }; } + // @ts-expect-error const handleAliases = async(ret, query, filterCrates, currentCrate) => { const lowerQuery = query.toLowerCase(); // We separate aliases and crate aliases because we want to have current crate // aliases to be before the others in the displayed results. + // @ts-expect-error const aliases = []; + // @ts-expect-error const crateAliases = []; if (filterCrates !== null) { if (this.ALIASES.has(filterCrates) @@ -3775,6 +4297,7 @@ class DocSearch { } else { for (const [crate, crateAliasesIndex] of this.ALIASES) { if (crateAliasesIndex.has(lowerQuery)) { + // @ts-expect-error const pushTo = crate === currentCrate ? crateAliases : aliases; const query_aliases = crateAliasesIndex.get(lowerQuery); for (const alias of query_aliases) { @@ -3784,6 +4307,7 @@ class DocSearch { } } + // @ts-expect-error const sortFunc = (aaa, bbb) => { if (aaa.path < bbb.path) { return 1; @@ -3792,18 +4316,23 @@ class DocSearch { } return -1; }; + // @ts-expect-error crateAliases.sort(sortFunc); aliases.sort(sortFunc); + // @ts-expect-error const fetchDesc = alias => { + // @ts-expect-error return this.searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? "" : this.searchState.loadDesc(alias); }; const [crateDescs, descs] = await Promise.all([ + // @ts-expect-error Promise.all(crateAliases.map(fetchDesc)), Promise.all(aliases.map(fetchDesc)), ]); + // @ts-expect-error const pushFunc = alias => { alias.alias = query; const res = buildHrefAndPath(alias); @@ -3818,12 +4347,15 @@ class DocSearch { }; aliases.forEach((alias, i) => { + // @ts-expect-error alias.desc = descs[i]; }); aliases.forEach(pushFunc); + // @ts-expect-error crateAliases.forEach((alias, i) => { alias.desc = crateDescs[i]; }); + // @ts-expect-error crateAliases.forEach(pushFunc); }; @@ -3843,21 +4375,24 @@ class DocSearch { * * `path_dist` is zero if a single-component search query is used, otherwise it's the * distance computed for everything other than the last path component. * - * @param {Results} results + * @param {rustdoc.Results} results * @param {string} fullId - * @param {integer} id - * @param {integer} index - * @param {integer} dist - * @param {integer} path_dist + * @param {number} id + * @param {number} index + * @param {number} dist + * @param {number} path_dist */ + // @ts-expect-error function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { if (dist <= maxEditDistance || index !== -1) { if (results.has(fullId)) { const result = results.get(fullId); + // @ts-expect-error if (result.dontValidate || result.dist <= dist) { return; } } + // @ts-expect-error results.set(fullId, { id: id, index: index, @@ -3873,12 +4408,16 @@ class DocSearch { * try to match the items which validates all the elements. For `aa -> bb` will look for * functions which have a parameter `aa` and has `bb` in its returned values. * - * @param {Row} row - * @param {integer} pos - Position in the `searchIndex`. - * @param {Object} results + * @param {rustdoc.Row} row + * @param {number} pos - Position in the `searchIndex`. + * @param {rustdoc.Results} results */ function handleArgs(row, pos, results) { - if (!row || (filterCrates !== null && row.crate !== filterCrates) || !row.type) { + if (!row || (filterCrates !== null && row.crate !== filterCrates)) { + return; + } + const rowType = row.type; + if (!rowType) { return; } @@ -3889,21 +4428,23 @@ class DocSearch { if (tfpDist === null) { return; } + // @ts-expect-error if (results.size >= MAX_RESULTS && tfpDist > results.max_dist) { return; } // If the result is too "bad", we return false and it ends this search. if (!unifyFunctionTypes( - row.type.inputs, + rowType.inputs, parsedQuery.elems, - row.type.where_clause, + rowType.where_clause, null, + // @ts-expect-error mgens => { return unifyFunctionTypes( - row.type.output, + rowType.output, parsedQuery.returned, - row.type.where_clause, + rowType.where_clause, mgens, checkTypeMgensForConflict, 0, // unboxing depth @@ -3914,15 +4455,16 @@ class DocSearch { return; } + // @ts-expect-error results.max_dist = Math.max(results.max_dist || 0, tfpDist); - addIntoResults(results, row.id, pos, 0, tfpDist, 0, Number.MAX_VALUE); + addIntoResults(results, row.id.toString(), pos, 0, tfpDist, 0, Number.MAX_VALUE); } /** * Compare the query fingerprint with the function fingerprint. * - * @param {{number}} fullId - The function - * @param {{Uint32Array}} queryFingerprint - The query + * @param {number} fullId - The function + * @param {Uint32Array} queryFingerprint - The query * @returns {number|null} - Null if non-match, number if distance * This function might return 0! */ @@ -3953,138 +4495,10 @@ class DocSearch { const innerRunQuery = () => { - const queryLen = - parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + - parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); - const maxEditDistance = Math.floor(queryLen / 3); - - /** - * @type {Map} - */ - const genericSymbols = new Map(); - - /** - * Convert names to ids in parsed query elements. - * This is not used for the "In Names" tab, but is used for the - * "In Params", "In Returns", and "In Function Signature" tabs. - * - * If there is no matching item, but a close-enough match, this - * function also that correction. - * - * See `buildTypeMapIndex` for more information. - * - * @param {QueryElement} elem - * @param {boolean} isAssocType - */ - const convertNameToId = (elem, isAssocType) => { - const loweredName = elem.pathLast.toLowerCase(); - if (this.typeNameIdMap.has(loweredName) && - (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { - elem.id = this.typeNameIdMap.get(loweredName).id; - } else if (!parsedQuery.literalSearch) { - let match = null; - let matchDist = maxEditDistance + 1; - let matchName = ""; - for (const [name, { id, assocOnly }] of this.typeNameIdMap) { - const dist = Math.min( - editDistance(name, loweredName, maxEditDistance), - editDistance(name, elem.normalizedPathLast, maxEditDistance), - ); - if (dist <= matchDist && dist <= maxEditDistance && - (isAssocType || !assocOnly)) { - if (dist === matchDist && matchName > name) { - continue; - } - match = id; - matchDist = dist; - matchName = name; - } - } - if (match !== null) { - parsedQuery.correction = matchName; - } - elem.id = match; - } - if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 - && elem.generics.length === 0 && elem.bindings.size === 0) - || elem.typeFilter === TY_GENERIC) { - if (genericSymbols.has(elem.normalizedPathLast)) { - elem.id = genericSymbols.get(elem.normalizedPathLast); - } else { - elem.id = -(genericSymbols.size + 1); - genericSymbols.set(elem.normalizedPathLast, elem.id); - } - if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { - // Silly heuristic to catch if the user probably meant - // to not write a generic parameter. We don't use it, - // just bring it up. - const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); - let matchDist = maxPartDistance + 1; - let matchName = ""; - for (const name of this.typeNameIdMap.keys()) { - const dist = editDistance( - name, - elem.normalizedPathLast, - maxPartDistance, - ); - if (dist <= matchDist && dist <= maxPartDistance) { - if (dist === matchDist && matchName > name) { - continue; - } - matchDist = dist; - matchName = name; - } - } - if (matchName !== "") { - parsedQuery.proposeCorrectionFrom = elem.name; - parsedQuery.proposeCorrectionTo = matchName; - } - } - elem.typeFilter = TY_GENERIC; - } - if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { - // Rust does not have HKT - parsedQuery.error = [ - "Generic type parameter ", - elem.name, - " does not accept generic parameters", - ]; - } - for (const elem2 of elem.generics) { - convertNameToId(elem2); - } - elem.bindings = new Map(Array.from(elem.bindings.entries()) - .map(entry => { - const [name, constraints] = entry; - if (!this.typeNameIdMap.has(name)) { - parsedQuery.error = [ - "Type parameter ", - name, - " does not exist", - ]; - return [null, []]; - } - for (const elem2 of constraints) { - convertNameToId(elem2); - } - - return [this.typeNameIdMap.get(name).id, constraints]; - }), - ); - }; - - for (const elem of parsedQuery.elems) { - convertNameToId(elem); - this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); - } - for (const elem of parsedQuery.returned) { - convertNameToId(elem); - this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); - } - if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) { const elem = parsedQuery.elems[0]; // use arrow functions to preserve `this`. + // @ts-expect-error const handleNameSearch = id => { const row = this.searchIndex[id]; if (!typePassesFilter(elem.typeFilter, row.ty) || @@ -4094,6 +4508,7 @@ class DocSearch { let pathDist = 0; if (elem.fullPath.length > 1) { + // @ts-expect-error pathDist = checkPath(elem.pathWithoutLast, row); if (pathDist === null) { return; @@ -4102,11 +4517,13 @@ class DocSearch { if (parsedQuery.literalSearch) { if (row.word === elem.pathLast) { + // @ts-expect-error addIntoResults(results_others, row.id, id, 0, 0, pathDist); } } else { addIntoResults( results_others, + // @ts-expect-error row.id, id, row.normalizedName.indexOf(elem.normalizedPathLast), @@ -4147,23 +4564,31 @@ class DocSearch { const returned = row.type && row.type.output && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); if (in_args) { + // @ts-expect-error results_in_args.max_dist = Math.max( + // @ts-expect-error results_in_args.max_dist || 0, tfpDist, ); const maxDist = results_in_args.size < MAX_RESULTS ? (tfpDist + 1) : + // @ts-expect-error results_in_args.max_dist; + // @ts-expect-error addIntoResults(results_in_args, row.id, i, -1, tfpDist, 0, maxDist); } if (returned) { + // @ts-expect-error results_returned.max_dist = Math.max( + // @ts-expect-error results_returned.max_dist || 0, tfpDist, ); const maxDist = results_returned.size < MAX_RESULTS ? (tfpDist + 1) : + // @ts-expect-error results_returned.max_dist; + // @ts-expect-error addIntoResults(results_returned, row.id, i, -1, tfpDist, 0, maxDist); } } @@ -4173,14 +4598,17 @@ class DocSearch { // types with generic parameters go last. // That's because of the way unification is structured: it eats off // the end, and hits a fast path if the last item is a simple atom. + // @ts-expect-error const sortQ = (a, b) => { const ag = a.generics.length === 0 && a.bindings.size === 0; const bg = b.generics.length === 0 && b.bindings.size === 0; if (ag !== bg) { + // @ts-expect-error return ag - bg; } const ai = a.id > 0; const bi = b.id > 0; + // @ts-expect-error return ai - bi; }; parsedQuery.elems.sort(sortQ); @@ -4197,8 +4625,11 @@ class DocSearch { const isType = parsedQuery.foundElems !== 1 || parsedQuery.hasReturnArrow; const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ + // @ts-expect-error sortResults(results_in_args, "elems", currentCrate), + // @ts-expect-error sortResults(results_returned, "returned", currentCrate), + // @ts-expect-error sortResults(results_others, (isType ? "query" : null), currentCrate), ]); const ret = createQueryResults( @@ -4210,11 +4641,14 @@ class DocSearch { filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { + // @ts-expect-error return this.searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? "" : + // @ts-expect-error this.searchState.loadDesc(result); })); for (const [i, result] of list.entries()) { + // @ts-expect-error result.desc = descs[i]; } })); @@ -4229,7 +4663,9 @@ class DocSearch { // ==================== Core search logic end ==================== +/** @type {Map} */ let rawSearchIndex; +// @ts-expect-error let docSearch; const longItemTypes = [ "keyword", @@ -4259,13 +4695,16 @@ const longItemTypes = [ "derive macro", "trait alias", ]; +// @ts-expect-error let currentResults; // In the search display, allows to switch between tabs. +// @ts-expect-error function printTab(nb) { let iter = 0; let foundCurrentTab = false; let foundCurrentResultSet = false; + // @ts-expect-error onEachLazy(document.getElementById("search-tabs").childNodes, elem => { if (nb === iter) { addClass(elem, "selected"); @@ -4277,6 +4716,7 @@ function printTab(nb) { }); const isTypeSearch = (nb > 0 || iter === 1); iter = 0; + // @ts-expect-error onEachLazy(document.getElementById("results").childNodes, elem => { if (nb === iter) { addClass(elem, "active"); @@ -4287,12 +4727,15 @@ function printTab(nb) { iter += 1; }); if (foundCurrentTab && foundCurrentResultSet) { + // @ts-expect-error searchState.currentTab = nb; // Corrections only kick in on type-based searches. const correctionsElem = document.getElementsByClassName("search-corrections"); if (isTypeSearch) { + // @ts-expect-error removeClass(correctionsElem[0], "hidden"); } else { + // @ts-expect-error addClass(correctionsElem[0], "hidden"); } } else if (nb !== 0) { @@ -4326,16 +4769,22 @@ function getFilterCrates() { const elem = document.getElementById("crate-search"); if (elem && + // @ts-expect-error elem.value !== "all crates" && + // @ts-expect-error window.searchIndex.has(elem.value) ) { + // @ts-expect-error return elem.value; } return null; } +// @ts-expect-error function nextTab(direction) { + // @ts-expect-error const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; + // @ts-expect-error searchState.focusedByTab[searchState.currentTab] = document.activeElement; printTab(next); focusSearchResult(); @@ -4344,9 +4793,12 @@ function nextTab(direction) { // Focus the first search result on the active tab, or the result that // was focused last time this tab was active. function focusSearchResult() { + // @ts-expect-error const target = searchState.focusedByTab[searchState.currentTab] || document.querySelectorAll(".search-results.active a").item(0) || + // @ts-expect-error document.querySelectorAll("#search-tabs button").item(searchState.currentTab); + // @ts-expect-error searchState.focusedByTab[searchState.currentTab] = null; if (target) { target.focus(); @@ -4356,9 +4808,8 @@ function focusSearchResult() { /** * Render a set of search results for a single tab. * @param {Array} array - The search results for this tab - * @param {ParsedQuery} query + * @param {rustdoc.ParsedQuery} query * @param {boolean} display - True if this is the active tab - * @param {"sig"|"elems"|"returned"|null} typeInfo */ async function addTab(array, query, display) { const extraClass = display ? " active" : ""; @@ -4405,6 +4856,7 @@ ${item.displayPath}${name}\ if (item.displayTypeSignature) { const {type, mappedNames, whereClause} = await item.displayTypeSignature; const displayType = document.createElement("div"); + // @ts-expect-error type.forEach((value, index) => { if (index % 2 !== 0) { const highlight = document.createElement("strong"); @@ -4445,6 +4897,7 @@ ${item.displayPath}${name}\ const line = document.createElement("div"); line.className = "where"; line.appendChild(document.createTextNode(` ${name}: `)); + // @ts-expect-error innerType.forEach((value, index) => { if (index % 2 !== 0) { const highlight = document.createElement("strong"); @@ -4488,6 +4941,7 @@ ${item.displayPath}${name}\ return output; } +// @ts-expect-error function makeTabHeader(tabNb, text, nbElems) { // https://blog.horizon-eda.org/misc/2020/02/19/ui.html // @@ -4497,6 +4951,7 @@ function makeTabHeader(tabNb, text, nbElems) { const fmtNbElems = nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : `\u{2007}(${nbElems})`; + // @ts-expect-error if (searchState.currentTab === tabNb) { return ""; @@ -4505,11 +4960,12 @@ function makeTabHeader(tabNb, text, nbElems) { } /** - * @param {ResultsTable} results + * @param {rustdoc.ResultsTable} results * @param {boolean} go_to_first * @param {string} filterCrates */ async function showResults(results, go_to_first, filterCrates) { + // @ts-expect-error const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -4527,6 +4983,7 @@ async function showResults(results, go_to_first, filterCrates) { // will be used, starting search again since the search input is not empty, leading you // back to the previous page again. window.onunload = () => { }; + // @ts-expect-error searchState.removeQueryParameters(); const elem = document.createElement("a"); elem.href = results.others[0].href; @@ -4537,6 +4994,7 @@ async function showResults(results, go_to_first, filterCrates) { return; } if (results.query === undefined) { + // @ts-expect-error results.query = DocSearch.parseQuery(searchState.input.value); } @@ -4545,6 +5003,7 @@ async function showResults(results, go_to_first, filterCrates) { // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on // it again. + // @ts-expect-error let currentTab = searchState.currentTab; if ((currentTab === 0 && results.others.length === 0) || (currentTab === 1 && results.in_args.length === 0) || @@ -4572,6 +5031,7 @@ async function showResults(results, go_to_first, filterCrates) {

Results

${crates}`; if (results.query.error !== null) { const error = results.query.error; + // @ts-expect-error error.forEach((value, index) => { value = value.split("<").join("<").split(">").join(">"); if (index % 2 !== 0) { @@ -4632,7 +5092,9 @@ async function showResults(results, go_to_first, filterCrates) { resultsElem.appendChild(ret_returned); search.innerHTML = output; + // @ts-expect-error if (searchState.rustdocToolbar) { + // @ts-expect-error search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar); } const crateSearch = document.getElementById("crate-search"); @@ -4641,23 +5103,30 @@ async function showResults(results, go_to_first, filterCrates) { } search.appendChild(resultsElem); // Reset focused elements. + // @ts-expect-error searchState.showResults(search); + // @ts-expect-error const elems = document.getElementById("search-tabs").childNodes; + // @ts-expect-error searchState.focusedByTab = []; let i = 0; for (const elem of elems) { const j = i; + // @ts-expect-error elem.onclick = () => printTab(j); + // @ts-expect-error searchState.focusedByTab.push(null); i += 1; } printTab(currentTab); } +// @ts-expect-error function updateSearchHistory(url) { if (!browserSupportsHistoryApi()) { return; } + // @ts-expect-error const params = searchState.getQueryStringParams(); if (!history.state && !params.search) { history.pushState(null, "", url); @@ -4672,9 +5141,11 @@ function updateSearchHistory(url) { * @param {boolean} [forced] */ async function search(forced) { + // @ts-expect-error const query = DocSearch.parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); + // @ts-expect-error if (!forced && query.userQuery === currentResults) { if (query.userQuery.length > 0) { putBackSearch(); @@ -4682,8 +5153,10 @@ async function search(forced) { return; } + // @ts-expect-error searchState.setLoadingSearch(); + // @ts-expect-error const params = searchState.getQueryStringParams(); // In case we have no information about the saved crate and there is a URL query parameter, @@ -4693,6 +5166,7 @@ async function search(forced) { } // Update document title to maintain a meaningful browser history + // @ts-expect-error searchState.title = "\"" + query.userQuery + "\" Search - Rust"; // Because searching is incremental by character, only the most @@ -4700,8 +5174,10 @@ async function search(forced) { updateSearchHistory(buildUrl(query.userQuery, filterCrates)); await showResults( + // @ts-expect-error await docSearch.execQuery(query, filterCrates, window.currentCrate), params.go_to_first, + // @ts-expect-error filterCrates); } @@ -4710,62 +5186,83 @@ async function search(forced) { * @param {Event} [e] - The event that triggered this call, if any */ function onSearchSubmit(e) { + // @ts-expect-error e.preventDefault(); + // @ts-expect-error searchState.clearInputTimeout(); search(); } function putBackSearch() { + // @ts-expect-error const search_input = searchState.input; + // @ts-expect-error if (!searchState.input) { return; } + // @ts-expect-error if (search_input.value !== "" && !searchState.isDisplayed()) { + // @ts-expect-error searchState.showResults(); if (browserSupportsHistoryApi()) { history.replaceState(null, "", buildUrl(search_input.value, getFilterCrates())); } + // @ts-expect-error document.title = searchState.title; } } function registerSearchEvents() { + // @ts-expect-error const params = searchState.getQueryStringParams(); // Populate search bar with query string search term when provided, // but only if the input bar is empty. This avoid the obnoxious issue // where you start trying to do a search, and the index loads, and // suddenly your search is gone! + // @ts-expect-error if (searchState.input.value === "") { + // @ts-expect-error searchState.input.value = params.search || ""; } const searchAfter500ms = () => { + // @ts-expect-error searchState.clearInputTimeout(); + // @ts-expect-error if (searchState.input.value.length === 0) { + // @ts-expect-error searchState.hideResults(); } else { + // @ts-expect-error searchState.timeout = setTimeout(search, 500); } }; + // @ts-expect-error searchState.input.onkeyup = searchAfter500ms; + // @ts-expect-error searchState.input.oninput = searchAfter500ms; + // @ts-expect-error document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; + // @ts-expect-error searchState.input.onchange = e => { if (e.target !== document.activeElement) { // To prevent doing anything when it's from a blur event. return; } // Do NOT e.preventDefault() here. It will prevent pasting. + // @ts-expect-error searchState.clearInputTimeout(); // zero-timeout necessary here because at the time of event handler execution the // pasted content is not in the input field yet. Shouldn’t make any difference for // change, though. setTimeout(search, 0); }; + // @ts-expect-error searchState.input.onpaste = searchState.input.onchange; + // @ts-expect-error searchState.outputElement().addEventListener("keydown", e => { // We only handle unmodified keystrokes here. We don't want to interfere with, // for instance, alt-left and alt-right for history navigation. @@ -4775,18 +5272,24 @@ function registerSearchEvents() { // up and down arrow select next/previous search result, or the // search box if we're already at the top. if (e.which === 38) { // up + // @ts-expect-error const previous = document.activeElement.previousElementSibling; if (previous) { + // @ts-expect-error previous.focus(); } else { + // @ts-expect-error searchState.focus(); } e.preventDefault(); } else if (e.which === 40) { // down + // @ts-expect-error const next = document.activeElement.nextElementSibling; if (next) { + // @ts-expect-error next.focus(); } + // @ts-expect-error const rect = document.activeElement.getBoundingClientRect(); if (window.innerHeight - rect.bottom < rect.height) { window.scrollBy(0, rect.height); @@ -4801,6 +5304,7 @@ function registerSearchEvents() { } }); + // @ts-expect-error searchState.input.addEventListener("keydown", e => { if (e.which === 40) { // down focusSearchResult(); @@ -4808,11 +5312,14 @@ function registerSearchEvents() { } }); + // @ts-expect-error searchState.input.addEventListener("focus", () => { putBackSearch(); }); + // @ts-expect-error searchState.input.addEventListener("blur", () => { + // @ts-expect-error searchState.input.placeholder = searchState.input.origPlaceholder; }); @@ -4823,6 +5330,7 @@ function registerSearchEvents() { const previousTitle = document.title; window.addEventListener("popstate", e => { + // @ts-expect-error const params = searchState.getQueryStringParams(); // Revert to the previous title manually since the History // API ignores the title parameter. @@ -4836,6 +5344,7 @@ function registerSearchEvents() { // nothing there, which lets you really go back to a // previous state with nothing in the bar. if (params.search && params.search.length > 0) { + // @ts-expect-error searchState.input.value = params.search; // Some browsers fire "onpopstate" for every page load // (Chrome), while others fire the event only when actually @@ -4845,9 +5354,11 @@ function registerSearchEvents() { e.preventDefault(); search(); } else { + // @ts-expect-error searchState.input.value = ""; // When browsing back from search results the main page // visibility must be reset. + // @ts-expect-error searchState.hideResults(); } }); @@ -4860,17 +5371,22 @@ function registerSearchEvents() { // that try to sync state between the URL and the search input. To work around it, // do a small amount of re-init on page show. window.onpageshow = () => { + // @ts-expect-error const qSearch = searchState.getQueryStringParams().search; + // @ts-expect-error if (searchState.input.value === "" && qSearch) { + // @ts-expect-error searchState.input.value = qSearch; } search(); }; } +// @ts-expect-error function updateCrate(ev) { if (ev.target.value === "all crates") { // If we don't remove it from the URL, it'll be picked up again by the search. + // @ts-expect-error const query = searchState.input.value.trim(); updateSearchHistory(buildUrl(query, null)); } @@ -4881,9 +5397,11 @@ function updateCrate(ev) { search(true); } +// @ts-expect-error function initSearch(searchIndx) { rawSearchIndex = searchIndx; if (typeof window !== "undefined") { + // @ts-expect-error docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); registerSearchEvents(); // If there's a search term in the URL, execute the search now. @@ -4891,6 +5409,7 @@ function initSearch(searchIndx) { search(); } } else if (typeof exports !== "undefined") { + // @ts-expect-error docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); exports.docSearch = docSearch; exports.parseQuery = DocSearch.parseQuery; @@ -4902,8 +5421,11 @@ if (typeof exports !== "undefined") { } if (typeof window !== "undefined") { + // @ts-expect-error window.initSearch = initSearch; + // @ts-expect-error if (window.searchIndex !== undefined) { + // @ts-expect-error initSearch(window.searchIndex); } } else { @@ -4918,19 +5440,23 @@ if (typeof window !== "undefined") { // https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/ // LevenshteinAutomata.java class ParametricDescription { + // @ts-expect-error constructor(w, n, minErrors) { this.w = w; this.n = n; this.minErrors = minErrors; } + // @ts-expect-error isAccept(absState) { const state = Math.floor(absState / (this.w + 1)); const offset = absState % (this.w + 1); return this.w - offset + this.minErrors[state] <= this.n; } + // @ts-expect-error getPosition(absState) { return absState % (this.w + 1); } + // @ts-expect-error getVector(name, charCode, pos, end) { let vector = 0; for (let i = pos; i < end; i += 1) { @@ -4941,6 +5467,7 @@ class ParametricDescription { } return vector; } + // @ts-expect-error unpack(data, index, bitsPerValue) { const bitLoc = (bitsPerValue * index); const dataLoc = bitLoc >> 5; diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 183663b94fc2..d7b0e4b4f541 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -3,6 +3,9 @@ /* global addClass, removeClass, onEach, onEachLazy */ /* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index 3003f4c15033..8f712f4c20c7 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -5,6 +5,9 @@ /* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage, getVar */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d77804d045e3..4770ccc12799 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -5,15 +5,28 @@ // the page, so we don't see major layout changes during the load of the page. "use strict"; +/** + * @import * as rustdoc from "./rustdoc.d.ts"; + */ + const builtinThemes = ["light", "dark", "ayu"]; const darkThemes = ["dark", "ayu"]; -window.currentTheme = document.getElementById("themeStyle"); +window.currentTheme = (function() { + const currentTheme = document.getElementById("themeStyle"); + return currentTheme instanceof HTMLLinkElement ? currentTheme : null; +})(); const settingsDataset = (function() { const settingsElement = document.getElementById("default-settings"); return settingsElement && settingsElement.dataset ? settingsElement.dataset : null; })(); +/** + * Get a configuration value. If it's not set, get the default. + * + * @param {string} settingName + * @returns + */ function getSettingValue(settingName) { const current = getCurrentValue(settingName); if (current === null && settingsDataset !== null) { @@ -29,17 +42,39 @@ function getSettingValue(settingName) { const localStoredTheme = getSettingValue("theme"); +/** + * Check if a DOM Element has the given class set. + * If `elem` is null, returns false. + * + * @param {HTMLElement|null} elem + * @param {string} className + * @returns {boolean} + */ // eslint-disable-next-line no-unused-vars function hasClass(elem, className) { - return elem && elem.classList && elem.classList.contains(className); + return !!elem && !!elem.classList && elem.classList.contains(className); } +/** + * Add a class to a DOM Element. If `elem` is null, + * does nothing. This function is idempotent. + * + * @param {HTMLElement|null} elem + * @param {string} className + */ function addClass(elem, className) { if (elem && elem.classList) { elem.classList.add(className); } } +/** + * Remove a class from a DOM Element. If `elem` is null, + * does nothing. This function is idempotent. + * + * @param {HTMLElement|null} elem + * @param {string} className + */ // eslint-disable-next-line no-unused-vars function removeClass(elem, className) { if (elem && elem.classList) { @@ -49,8 +84,8 @@ function removeClass(elem, className) { /** * Run a callback for every element of an Array. - * @param {Array} arr - The array to iterate over - * @param {function(?)} func - The callback + * @param {Array} arr - The array to iterate over + * @param {function(?): boolean|undefined} func - The callback */ function onEach(arr, func) { for (const elem of arr) { @@ -67,8 +102,8 @@ function onEach(arr, func) { * or a "live" NodeList while modifying it can be very slow. * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection * https://developer.mozilla.org/en-US/docs/Web/API/NodeList - * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over - * @param {function(?)} func - The callback + * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over + * @param {function(?): boolean} func - The callback */ // eslint-disable-next-line no-unused-vars function onEachLazy(lazyArray, func) { @@ -77,6 +112,15 @@ function onEachLazy(lazyArray, func) { func); } +/** + * Set a configuration value. This uses localstorage, + * with a `rustdoc-` prefix, to avoid clashing with other + * web apps that may be running in the same domain (for example, mdBook). + * If localStorage is disabled, this function does nothing. + * + * @param {string} name + * @param {string} value + */ function updateLocalStorage(name, value) { try { window.localStorage.setItem("rustdoc-" + name, value); @@ -85,6 +129,15 @@ function updateLocalStorage(name, value) { } } +/** + * Get a configuration value. If localStorage is disabled, + * this function returns null. If the setting was never + * changed by the user, it also returns null; if you want to + * be able to use a default value, call `getSettingValue` instead. + * + * @param {string} name + * @returns {string|null} + */ function getCurrentValue(name) { try { return window.localStorage.getItem("rustdoc-" + name); @@ -93,19 +146,29 @@ function getCurrentValue(name) { } } -// Get a value from the rustdoc-vars div, which is used to convey data from -// Rust to the JS. If there is no such element, return null. -const getVar = (function getVar(name) { +/** + * Get a value from the rustdoc-vars div, which is used to convey data from + * Rust to the JS. If there is no such element, return null. + * + * @param {string} name + * @returns {string|null} + */ +function getVar(name) { const el = document.querySelector("head > meta[name='rustdoc-vars']"); - return el ? el.attributes["data-" + name].value : null; -}); + return el ? el.getAttribute("data-" + name) : null; +} +/** + * Change the current theme. + * @param {string|null} newThemeName + * @param {boolean} saveTheme + */ function switchTheme(newThemeName, saveTheme) { - const themeNames = getVar("themes").split(",").filter(t => t); + const themeNames = (getVar("themes") || "").split(",").filter(t => t); themeNames.push(...builtinThemes); // Ensure that the new theme name is among the defined themes - if (themeNames.indexOf(newThemeName) === -1) { + if (newThemeName === null || themeNames.indexOf(newThemeName) === -1) { return; } @@ -118,7 +181,7 @@ function switchTheme(newThemeName, saveTheme) { document.documentElement.setAttribute("data-theme", newThemeName); if (builtinThemes.indexOf(newThemeName) !== -1) { - if (window.currentTheme) { + if (window.currentTheme && window.currentTheme.parentNode) { window.currentTheme.parentNode.removeChild(window.currentTheme); window.currentTheme = null; } @@ -130,7 +193,10 @@ function switchTheme(newThemeName, saveTheme) { // rendering, but if we are done, it would blank the page. if (document.readyState === "loading") { document.write(``); - window.currentTheme = document.getElementById("themeStyle"); + window.currentTheme = (function() { + const currentTheme = document.getElementById("themeStyle"); + return currentTheme instanceof HTMLLinkElement ? currentTheme : null; + })(); } else { window.currentTheme = document.createElement("link"); window.currentTheme.rel = "stylesheet"; @@ -179,11 +245,13 @@ const updateTheme = (function() { return updateTheme; })(); +// @ts-ignore if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // update the preferred dark theme if the user is already using a dark theme // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732 if (getSettingValue("use-system-theme") === null && getSettingValue("preferred-dark-theme") === null + && localStoredTheme !== null && darkThemes.indexOf(localStoredTheme) >= 0) { updateLocalStorage("preferred-dark-theme", localStoredTheme); } diff --git a/src/librustdoc/html/static/js/tsconfig.json b/src/librustdoc/html/static/js/tsconfig.json new file mode 100644 index 000000000000..b81099bb9dfd --- /dev/null +++ b/src/librustdoc/html/static/js/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2023", + "module": "esnext", + "rootDir": "./", + "allowJs": true, + "checkJs": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true + }, + "typeAcquisition": { + "include": ["./rustdoc.d.ts"] + } +} From 0a9ee02d0a3ac8a45ab00b817c801f087a06828e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 28 Jan 2025 03:44:03 +0100 Subject: [PATCH 188/342] GCI: Don't try to collect mono items inside overly generic free const items --- compiler/rustc_monomorphize/src/collector.rs | 11 +++++++---- .../def-site-eval.fail.stderr | 11 +++++++++++ tests/ui/generic-const-items/def-site-eval.rs | 16 ++++++++++++++++ tests/ui/generic-const-items/def-site-mono.rs | 13 +++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 tests/ui/generic-const-items/def-site-eval.fail.stderr create mode 100644 tests/ui/generic-const-items/def-site-eval.rs create mode 100644 tests/ui/generic-const-items/def-site-mono.rs diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bb603df11294..d53848f7461f 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1454,11 +1454,14 @@ impl<'v> RootCollector<'_, 'v> { self.output.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { - // const items only generate mono items if they are - // actually used somewhere. Just declaring them is insufficient. + // Const items only generate mono items if they are actually used somewhere. + // Just declaring them is insufficient. - // but even just declaring them must collect the items they refer to - if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) { + // But even just declaring them must collect the items they refer to + // unless their generics require monomorphization. + if !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) + && let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) + { collect_const_value(self.tcx, val, self.output); } } diff --git a/tests/ui/generic-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr new file mode 100644 index 000000000000..22a5f2916977 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.fail.stderr @@ -0,0 +1,11 @@ +error[E0080]: evaluation of `_::<'_>` failed + --> $DIR/def-site-eval.rs:14:20 + | +LL | const _<'_a>: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/def-site-eval.rs:14:20 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/generic-const-items/def-site-eval.rs b/tests/ui/generic-const-items/def-site-eval.rs new file mode 100644 index 000000000000..3ed7f96aed02 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.rs @@ -0,0 +1,16 @@ +//! Test that we only evaluate free const items (their def site to be clear) +//! whose generics don't require monomorphization. +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ revisions: fail pass +//@[fail] build-fail (we require monomorphization) +//@[pass] build-pass (we require monomorphization) + +const _<_T>: () = panic!(); +const _: () = panic!(); + +#[cfg(fail)] +const _<'_a>: () = panic!(); //[fail]~ ERROR evaluation of `_::<'_>` failed + +fn main() {} diff --git a/tests/ui/generic-const-items/def-site-mono.rs b/tests/ui/generic-const-items/def-site-mono.rs new file mode 100644 index 000000000000..f10d450f6bdb --- /dev/null +++ b/tests/ui/generic-const-items/def-site-mono.rs @@ -0,0 +1,13 @@ +//! Ensure that we don't try to collect monomorphizeable items inside free const +//! items (their def site to be clear) whose generics require monomorphization. +//! +//! Such items are to be collected at instantiation sites of free consts. + +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ build-pass (we require monomorphization) + +const _IDENTITY: fn(T) -> T = |x| x; + +fn main() {} From 93ee180cfa12cfca5e0ce79bb3d9a3eaf91cf7b5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jan 2025 18:30:17 +0100 Subject: [PATCH 189/342] ABI-required target features: warn when they are missing in base CPU (rather than silently enabling them) --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 50 +---------------- compiler/rustc_codegen_gcc/src/lib.rs | 5 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 56 ++----------------- compiler/rustc_interface/messages.ftl | 5 ++ compiler/rustc_interface/src/errors.rs | 9 +++ compiler/rustc_interface/src/interface.rs | 2 + compiler/rustc_interface/src/util.rs | 38 ++++++++++++- .../x86/intrinsics-x86-pause-without-sse2.rs | 4 +- tests/codegen/target-feature-overrides.rs | 8 +-- tests/codegen/tied-features-strength.rs | 5 +- tests/ui-fulldeps/codegen-backend/hotplug.rs | 4 ++ .../feature-hierarchy.aarch64-sve2.stderr | 7 --- ...target-feature-flag-disable-implied.stderr | 2 +- ...at-target-feature-flag-disable-neon.stderr | 2 +- ...rdfloat-target-feature-flag-disable.stderr | 10 ++-- 15 files changed, 80 insertions(+), 127 deletions(-) delete mode 100644 tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 560aff43d653..4e8c8aaaf5c8 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,10 +1,8 @@ -use std::iter::FromIterator; - #[cfg(feature = "master")] use gccjit::Context; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; @@ -45,12 +43,6 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Vec Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter(|(feature, _, _)| { // skip checking special features, as LLVM may not understand them if RUSTC_SPECIAL_FEATURES.contains(feature) { @@ -388,9 +387,13 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter_map(|(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { + // The `allow_unstable` set is used by rustc internally to determined which target + // features are truly available, so we want to return even perma-unstable "forbidden" + // features. + if allow_unstable + || (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { Some(*feature) } else { None @@ -670,12 +673,6 @@ pub(crate) fn global_llvm_features( // Will only be filled when `diagnostics` is set! let mut featsmap = FxHashMap::default(); - // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones - // are disabled. - let abi_feature_constraints = sess.target.abi_required_features(); - let abi_incompatible_set = - FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); - // Compute implied features let mut all_rust_features = vec![]; for feature in sess.opts.cg.target_feature.split(',') { @@ -746,52 +743,11 @@ pub(crate) fn global_llvm_features( } } - // Ensure that the features we enable/disable are compatible with the ABI. - if enable { - if abi_incompatible_set.contains(feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: "enabled", - reason: "this feature is incompatible with the target ABI", - }); - } - } else { - // FIXME: we have to request implied features here since - // negative features do not handle implied features above. - for &required in abi_feature_constraints.required.iter() { - let implied = - sess.target.implied_target_features(std::iter::once(required)); - if implied.contains(feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: "disabled", - reason: "this feature is required by the target ABI", - }); - } - } - } - // FIXME(nagisa): figure out how to not allocate a full hashset here. featsmap.insert(feature, enable); } } - // To be sure the ABI-relevant features are all in the right state, we explicitly - // (un)set them here. This means if the target spec sets those features wrong, - // we will silently correct them rather than silently producing wrong code. - // (The target sanity check tries to catch this, but we can't know which features are - // enabled in LLVM by default so we can't be fully sure about that check.) - // We add these at the beginning of the list so that `-Ctarget-features` can - // still override it... that's unsound, but more compatible with past behavior. - all_rust_features.splice( - 0..0, - abi_feature_constraints - .required - .iter() - .map(|&f| (true, f)) - .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), - ); - // Translate this into LLVM features. let feats = all_rust_features .iter() diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 47dfbc1d7fbf..31123625369b 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -1,3 +1,8 @@ +interface_abi_required_feature = + target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +interface_abi_required_feature_issue = for more information, see issue #116344 + interface_cant_emit_mir = could not emit MIR: {$error} diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 939980a932fd..b62950d67096 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -103,3 +103,12 @@ pub struct IgnoringOutDir; #[derive(Diagnostic)] #[diag(interface_multiple_output_types_to_stdout)] pub struct MultipleOutputTypesToStdout; + +#[derive(Diagnostic)] +#[diag(interface_abi_required_feature)] +#[note] +#[note(interface_abi_required_feature_issue)] +pub(crate) struct AbiRequiredTargetFeature<'a> { + pub feature: &'a str, + pub enabled: &'a str, +} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 2113345eda3a..d9803236f85c 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -492,6 +492,8 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se } sess.lint_store = Some(Lrc::new(lint_store)); + util::check_abi_required_features(&sess); + let compiler = Compiler { sess, codegen_backend, diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 984b8104f539..e900ec14fcab 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,21 +18,25 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use rustc_target::spec::Target; use tracing::info; use crate::errors; /// Function pointer type that constructs a new CodegenBackend. -pub type MakeBackendFn = fn() -> Box; +type MakeBackendFn = fn() -> Box; /// Adds `target_feature = "..."` cfgs for a variety of platform /// specific features (SSE, NEON etc.). /// /// This is performed by checking whether a set of permitted features /// is available on the target machine, by querying the codegen backend. -pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) { +pub(crate) fn add_configuration( + cfg: &mut Cfg, + sess: &mut Session, + codegen_backend: &dyn CodegenBackend, +) { let tf = sym::target_feature; let unstable_target_features = codegen_backend.target_features_cfg(sess, true); @@ -48,6 +52,34 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy } } +/// Ensures that all target features required by the ABI are present. +/// Must be called after `unstable_target_features` has been populated! +pub(crate) fn check_abi_required_features(sess: &Session) { + let abi_feature_constraints = sess.target.abi_required_features(); + // We check this against `unstable_target_features` as that is conveniently already + // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. + // Just double-check that the features we care about are actually on our list. + for feature in + abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter()) + { + assert!( + sess.target.rust_target_features().iter().any(|(name, ..)| feature == name), + "target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target" + ); + } + + for feature in abi_feature_constraints.required { + if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" }); + } + } + for feature in abi_feature_constraints.incompatible { + if sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" }); + } + } +} + pub static STACK_SIZE: OnceLock = OnceLock::new(); pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs index 4d5ddd75f385..6ca53c0eb6fc 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs @@ -1,5 +1,5 @@ -// We're testing x86 target specific features -//@only-target: x86_64 i686 +// We're testing x86-32 target specific features. SSE always exists on x86-64. +//@only-target: i686 //@compile-flags: -C target-feature=-sse2 #[cfg(target_arch = "x86")] diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index e7f70a1e24ab..f38a1ae72de5 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 6daa5cd7d5e2..1b2b63c3d1ac 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -11,11 +11,10 @@ // ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 -// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } +// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-sve,?)|(\+neon,?))*}}" } //@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0 -// `neon` and `fp-armv8` get enabled as target base features, but then disabled again at the end of the list. -// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}},-neon,-fp-armv8{{(,\+fpmr)?}}" } +// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-fp-armv8,?)|(-neon,?))*}}" } //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" } diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.rs b/tests/ui-fulldeps/codegen-backend/hotplug.rs index 917b20fcdb56..ce310ba3e315 100644 --- a/tests/ui-fulldeps/codegen-backend/hotplug.rs +++ b/tests/ui-fulldeps/codegen-backend/hotplug.rs @@ -6,6 +6,10 @@ //@ normalize-stdout: "libthe_backend.dylib" -> "libthe_backend.so" //@ normalize-stdout: "the_backend.dll" -> "libthe_backend.so" +// Pick a target that requires no target features, so that no warning is shown +// about missing target features. +//@ compile-flags: --target arm-unknown-linux-gnueabi +//@ needs-llvm-components: arm //@ revisions: normal dep bindep //@ compile-flags: --crate-type=lib //@ [normal] compile-flags: --emit=link=- diff --git a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr deleted file mode 100644 index b6c3ccdedfb0..000000000000 --- a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr +++ /dev/null @@ -1,7 +0,0 @@ -warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI - | - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #116344 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr index 72b2d03fe203..7ec8b04cfce0 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr @@ -1,4 +1,4 @@ -warning: target feature `sse` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr index b6c3ccdedfb0..b1186d5d5dc7 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `neon` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr index 6191681286a3..02398d27501c 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr @@ -1,11 +1,11 @@ -warning: unstable feature specified for `-Ctarget-feature`: `x87` - | - = note: this feature is not stably supported; its behavior can change in the future - -warning: target feature `x87` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + warning: 2 warnings emitted From 3f6ffa1462d8f9d4f6f645a330872b172074a2c5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jan 2025 20:17:39 +0100 Subject: [PATCH 190/342] update comments --- compiler/rustc_target/src/target_features.rs | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa..3bc4b92987f1 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -108,21 +108,19 @@ impl Stability { // per-function level, since we would then allow safe calls from functions with `+soft-float` to // functions without that feature! // -// It is important for soundness that features allowed here do *not* change the function call ABI. -// For example, disabling the `x87` feature on x86 changes how scalar floats are passed as -// arguments, so enabling toggling that feature would be unsound. In fact, since `-Ctarget-feature` -// will just allow unknown features (with a warning), we have to explicitly list features that change -// the ABI as `Forbidden` to ensure using them causes an error. Note that this is only effective if -// such features can never be toggled via `-Ctarget-cpu`! If that is ever a possibility, we will need -// extra checks ensuring that the LLVM-computed target features for a CPU did not (un)set a -// `Forbidden` feature. See https://github.com/rust-lang/rust/issues/116344 for some more context. -// FIXME: add such "forbidden" features for non-x86 targets. +// It is important for soundness to consider the interaction of targets features and the function +// call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as +// arguments, so letting people toggle that feature would be unsound. To this end, the +// `abi_required_features` function computes which target features must and must not be enabled for +// any given target, and individual features can also be marked as `Forbidden`. +// See https://github.com/rust-lang/rust/issues/116344 for some more context. // // The one exception to features that change the ABI is features that enable larger vector -// registers. Those are permitted to be listed here. This is currently unsound (see -// https://github.com/rust-lang/rust/issues/116558); in the future we will have to ensure that -// functions can only use such vectors as arguments/return types if the corresponding target feature -// is enabled. +// registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store +// information about which target feature is ABI-required for which vector size; this is used to +// ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For +// the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.) +// Also see https://github.com/rust-lang/rust/issues/116558. // // Stabilizing a target feature requires t-lang approval. From bf9df97660f2360bff4acf5231c5396eeaf1d3db Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 5 Dec 2024 12:51:19 -0500 Subject: [PATCH 191/342] Remove -Zinline-in-all-cgus and clean up CGU partitioning tests --- compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_middle/src/mir/mono.rs | 13 +-- compiler/rustc_session/src/options.rs | 2 - .../drop_in_place_intrinsic.rs | 3 +- .../item-collection/generic-drop-glue.rs | 3 +- .../instantiation-through-vtable.rs | 3 +- .../item-collection/non-generic-drop-glue.rs | 3 +- .../item-collection/transitive-drop-glue.rs | 3 +- .../item-collection/tuple-drop-glue.rs | 3 +- .../codegen-units/item-collection/unsizing.rs | 1 - tests/codegen-units/partitioning/README.md | 14 +++ .../auxiliary/shared_generics_aux.rs | 3 +- .../partitioning/extern-drop-glue.rs | 9 +- .../partitioning/extern-generic.rs | 39 +++---- .../partitioning/incremental-merging.rs | 9 +- .../partitioning/inline-always.rs | 38 +++++++ .../inlining-from-extern-crate.rs | 4 +- .../partitioning/local-drop-glue.rs | 16 +-- .../partitioning/local-generic.rs | 29 ++--- .../local-inlining-but-not-all.rs | 38 ------- .../partitioning/local-inlining.rs | 39 ------- .../partitioning/local-transitive-inlining.rs | 12 ++- .../methods-are-with-self-type.rs | 6 +- .../partitioning/regular-modules.rs | 102 +++++++++--------- .../partitioning/shared-generics.rs | 8 +- tests/codegen-units/partitioning/statics.rs | 6 +- .../partitioning/vtable-through-const.rs | 20 ++-- tests/run-make/sepcomp-cci-copies/cci_lib.rs | 6 -- tests/run-make/sepcomp-cci-copies/foo.rs | 25 ----- tests/run-make/sepcomp-cci-copies/rmake.rs | 14 --- tests/run-make/sepcomp-inlining/foo.rs | 29 ----- tests/run-make/sepcomp-inlining/rmake.rs | 20 ---- tests/run-make/sepcomp-separate/foo.rs | 21 ---- tests/run-make/sepcomp-separate/rmake.rs | 12 --- 34 files changed, 187 insertions(+), 367 deletions(-) create mode 100644 tests/codegen-units/partitioning/README.md create mode 100644 tests/codegen-units/partitioning/inline-always.rs delete mode 100644 tests/codegen-units/partitioning/local-inlining-but-not-all.rs delete mode 100644 tests/codegen-units/partitioning/local-inlining.rs delete mode 100644 tests/run-make/sepcomp-cci-copies/cci_lib.rs delete mode 100644 tests/run-make/sepcomp-cci-copies/foo.rs delete mode 100644 tests/run-make/sepcomp-cci-copies/rmake.rs delete mode 100644 tests/run-make/sepcomp-inlining/foo.rs delete mode 100644 tests/run-make/sepcomp-inlining/rmake.rs delete mode 100644 tests/run-make/sepcomp-separate/foo.rs delete mode 100644 tests/run-make/sepcomp-separate/rmake.rs diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index d7370c1ff53f..96bd6fc10a5a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -795,7 +795,6 @@ fn test_unstable_options_tracking_hash() { tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); - tracked!(inline_in_all_cgus, Some(true)); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 111c3b6956a2..327405d75379 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -91,13 +91,8 @@ impl<'tcx> MonoItem<'tcx> { } pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode { - let generate_cgu_internal_copies = tcx - .sess - .opts - .unstable_opts - .inline_in_all_cgus - .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && !tcx.sess.link_dead_code(); + let generate_cgu_internal_copies = + (tcx.sess.opts.optimize != OptLevel::No) && !tcx.sess.link_dead_code(); match *self { MonoItem::Fn(ref instance) => { @@ -121,8 +116,8 @@ impl<'tcx> MonoItem<'tcx> { } // At this point we don't have explicit linkage and we're an - // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. + // inlined function. If this crate's build settings permit, + // we'll be creating a local copy per CGU. if generate_cgu_internal_copies { return InstantiationMode::LocalCopy; } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 63aaa3abc8e5..4ce638251297 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1870,8 +1870,6 @@ options! { "verify extended properties for incr. comp. (default: no): - hashes of green query instances - hash collisions of query keys"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], diff --git a/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs b/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs index e1887b93b93a..a57b102a5fd2 100644 --- a/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/tests/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -1,7 +1,6 @@ -// //@ compile-flags:-Zprint-mono-items=eager -//@ compile-flags:-Zinline-in-all-cgus //@ compile-flags:-Zinline-mir=no +//@ compile-flags: -O #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/generic-drop-glue.rs b/tests/codegen-units/item-collection/generic-drop-glue.rs index 6ecf98a032f5..c0d2e899bbc8 100644 --- a/tests/codegen-units/item-collection/generic-drop-glue.rs +++ b/tests/codegen-units/item-collection/generic-drop-glue.rs @@ -1,6 +1,5 @@ -// //@ compile-flags:-Zprint-mono-items=eager -//@ compile-flags:-Zinline-in-all-cgus +//@ compile-flags: -O #![deny(dead_code)] #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/instantiation-through-vtable.rs b/tests/codegen-units/item-collection/instantiation-through-vtable.rs index 9087fc6410ab..ee0b5dc1dd2c 100644 --- a/tests/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/tests/codegen-units/item-collection/instantiation-through-vtable.rs @@ -1,5 +1,4 @@ -// -//@ compile-flags:-Zprint-mono-items=eager -Zinline-in-all-cgus -Zmir-opt-level=0 +//@ compile-flags:-Zprint-mono-items=eager -Zmir-opt-level=0 #![deny(dead_code)] #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/non-generic-drop-glue.rs b/tests/codegen-units/item-collection/non-generic-drop-glue.rs index c4d7942ba1ed..2eeccd2e99f2 100644 --- a/tests/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/tests/codegen-units/item-collection/non-generic-drop-glue.rs @@ -1,6 +1,5 @@ -// //@ compile-flags:-Zprint-mono-items=eager -//@ compile-flags:-Zinline-in-all-cgus +//@ compile-flags: -O #![deny(dead_code)] #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/transitive-drop-glue.rs b/tests/codegen-units/item-collection/transitive-drop-glue.rs index 18954fab86f6..b999e466d54b 100644 --- a/tests/codegen-units/item-collection/transitive-drop-glue.rs +++ b/tests/codegen-units/item-collection/transitive-drop-glue.rs @@ -1,6 +1,5 @@ -// //@ compile-flags:-Zprint-mono-items=eager -//@ compile-flags:-Zinline-in-all-cgus +//@ compile-flags: -O #![deny(dead_code)] #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/tuple-drop-glue.rs b/tests/codegen-units/item-collection/tuple-drop-glue.rs index 2e70d0151eb7..5e97fbb43368 100644 --- a/tests/codegen-units/item-collection/tuple-drop-glue.rs +++ b/tests/codegen-units/item-collection/tuple-drop-glue.rs @@ -1,6 +1,5 @@ -// //@ compile-flags:-Zprint-mono-items=eager -//@ compile-flags:-Zinline-in-all-cgus +//@ compile-flags: -O #![deny(dead_code)] #![crate_type = "lib"] diff --git a/tests/codegen-units/item-collection/unsizing.rs b/tests/codegen-units/item-collection/unsizing.rs index 23e6003dc19f..97adf72ce2c9 100644 --- a/tests/codegen-units/item-collection/unsizing.rs +++ b/tests/codegen-units/item-collection/unsizing.rs @@ -1,5 +1,4 @@ //@ compile-flags:-Zprint-mono-items=eager -//@ compile-flags:-Zinline-in-all-cgus //@ compile-flags:-Zmir-opt-level=0 #![deny(dead_code)] diff --git a/tests/codegen-units/partitioning/README.md b/tests/codegen-units/partitioning/README.md new file mode 100644 index 000000000000..5dd6b1281fd9 --- /dev/null +++ b/tests/codegen-units/partitioning/README.md @@ -0,0 +1,14 @@ +# codegen-units/partitioning tests + +This test suite is designed to test that codegen unit partitioning works as intended. +Note that it does not evaluate whether CGU partitioning is *good*. That is the job of the compiler benchmark suite. + +All tests in this suite use the flag `-Zprint-mono-items=lazy`, which makes the compiler print a machine-readable summary of all MonoItems that were collected, which CGUs they were assigned to, and the linkage in each CGU. The output looks like: +``` +MONO_ITEM @@ [] [] +``` +DO NOT add tests to this suite that use `-Zprint-mono-items=eager`. That flag changes the way that MonoItem collection works in rather fundamental ways that are otherwise only used by `-Clink-dead-code`, and thus the MonoItems collected and their linkage under `-Zprint-mono-items=eager` does not correlate very well with normal compilation behavior. + +The current CGU partitioning algorithm essentially groups MonoItems by which module they are defined in, then merges small CGUs. There are a lot of inline modules in this test suite because that's the only way to observe the partitioning. + +Currently, the test suite is very heavily biased towards incremental builds with -Copt-level=0. This is mostly an accident of history; the entire test suite was added as part of supporting incremental compilation in #32779. But also CGU partitioning is *mostly* valuable because the CGU is the unit of incrementality to the codegen backend (cached queries are the unit of incrementality for the rest of the compiler). diff --git a/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs b/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs index b6c568ed387a..d3517df03763 100644 --- a/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs +++ b/tests/codegen-units/partitioning/auxiliary/shared_generics_aux.rs @@ -1,7 +1,6 @@ // NOTE: We always compile this test with -Copt-level=0 because higher opt-levels // prevent drop-glue from participating in share-generics. -//@ compile-flags:-Zshare-generics=yes -Copt-level=0 -//@ no-prefer-dynamic +//@ compile-flags: -Zshare-generics=yes -Copt-level=0 #![crate_type = "rlib"] diff --git a/tests/codegen-units/partitioning/extern-drop-glue.rs b/tests/codegen-units/partitioning/extern-drop-glue.rs index d3bce7b4223c..ca78c175dbf3 100644 --- a/tests/codegen-units/partitioning/extern-drop-glue.rs +++ b/tests/codegen-units/partitioning/extern-drop-glue.rs @@ -1,15 +1,14 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation -// We specify opt-level=0 because `drop_in_place` is `Internal` when optimizing //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus -Copt-level=0 +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -#![allow(dead_code)] #![crate_type = "rlib"] //@ aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; +// This test checks that drop glue is generated, even for types not defined in this crate, and all +// drop glue is put in the fallback CGU. + //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(cgu_extern_drop_glue::Struct)) @@ extern_drop_glue-fallback.cgu[External] struct LocalStruct(cgu_extern_drop_glue::Struct); diff --git a/tests/codegen-units/partitioning/extern-generic.rs b/tests/codegen-units/partitioning/extern-generic.rs index 602a5240283b..875ebb3098e5 100644 --- a/tests/codegen-units/partitioning/extern-generic.rs +++ b/tests/codegen-units/partitioning/extern-generic.rs @@ -1,51 +1,36 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=eager -Zshare-generics=y +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -#![allow(dead_code)] #![crate_type = "lib"] //@ aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ MONO_ITEM fn user @@ extern_generic[Internal] -fn user() { +// This test checks that, in an unoptimized build, a generic function and its callees are only +// instantiated once in this crate. + +//~ MONO_ITEM fn user @@ extern_generic[External] +pub fn user() { let _ = cgu_generic_function::foo("abc"); } -mod mod1 { +pub mod mod1 { use cgu_generic_function; - //~ MONO_ITEM fn mod1::user @@ extern_generic-mod1[Internal] - fn user() { + //~ MONO_ITEM fn mod1::user @@ extern_generic-mod1[External] + pub fn user() { let _ = cgu_generic_function::foo("abc"); } - mod mod1 { + pub mod mod1 { use cgu_generic_function; - //~ MONO_ITEM fn mod1::mod1::user @@ extern_generic-mod1-mod1[Internal] - fn user() { + //~ MONO_ITEM fn mod1::mod1::user @@ extern_generic-mod1-mod1[External] + pub fn user() { let _ = cgu_generic_function::foo("abc"); } } } -mod mod2 { - use cgu_generic_function; - - //~ MONO_ITEM fn mod2::user @@ extern_generic-mod2[Internal] - fn user() { - let _ = cgu_generic_function::foo("abc"); - } -} - -mod mod3 { - //~ MONO_ITEM fn mod3::non_user @@ extern_generic-mod3[Internal] - fn non_user() {} -} - -// Make sure the two generic functions from the extern crate get instantiated -// once for the current crate //~ MONO_ITEM fn cgu_generic_function::foo::<&str> @@ cgu_generic_function-in-extern_generic.volatile[External] //~ MONO_ITEM fn cgu_generic_function::bar::<&str> @@ cgu_generic_function-in-extern_generic.volatile[External] diff --git a/tests/codegen-units/partitioning/incremental-merging.rs b/tests/codegen-units/partitioning/incremental-merging.rs index 6834bb2bebf5..68eee803e5f2 100644 --- a/tests/codegen-units/partitioning/incremental-merging.rs +++ b/tests/codegen-units/partitioning/incremental-merging.rs @@ -1,7 +1,5 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Ccodegen-units=3 +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -Ccodegen-units=3 #![crate_type = "rlib"] @@ -9,8 +7,9 @@ // compilation but at the same time does not modify names of CGUs that were not // affected by merging. // -// We expect CGUs `aaa` and `bbb` to be merged (because they are the smallest), -// while `ccc` and `ddd` are supposed to stay untouched. +// CGU partitioning creates one CGU per module, so with 4 modules and codegen-units=3, +// two of the modules should be merged. We expect CGUs `aaa` and `bbb` to be merged +// (because they are the smallest), while `ccc` and `ddd` should stay untouched. pub mod aaa { //~ MONO_ITEM fn aaa::foo @@ incremental_merging-aaa--incremental_merging-bbb[External] diff --git a/tests/codegen-units/partitioning/inline-always.rs b/tests/codegen-units/partitioning/inline-always.rs new file mode 100644 index 000000000000..5e8cce0ac337 --- /dev/null +++ b/tests/codegen-units/partitioning/inline-always.rs @@ -0,0 +1,38 @@ +//@ incremental +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 + +#![crate_type = "lib"] + +// This test checks that a monomorphic inline(always) function is instantiated in every CGU that +// references it, even though this is an unoptimized incremental build. +// It also checks that an inline(always) function is only placed in CGUs that reference it. + +mod inline { + //~ MONO_ITEM fn inline::inlined_function @@ inline_always-user1[Internal] inline_always-user2[Internal] + #[inline(always)] + pub fn inlined_function() {} +} + +pub mod user1 { + use super::inline; + + //~ MONO_ITEM fn user1::foo @@ inline_always-user1[External] + pub fn foo() { + inline::inlined_function(); + } +} + +pub mod user2 { + use super::inline; + + //~ MONO_ITEM fn user2::bar @@ inline_always-user2[External] + pub fn bar() { + inline::inlined_function(); + } +} + +pub mod non_user { + + //~ MONO_ITEM fn non_user::baz @@ inline_always-non_user[External] + pub fn baz() {} +} diff --git a/tests/codegen-units/partitioning/inlining-from-extern-crate.rs b/tests/codegen-units/partitioning/inlining-from-extern-crate.rs index b007ffe1cb51..d321c88d03a6 100644 --- a/tests/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/tests/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -1,7 +1,5 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=1 #![crate_type = "lib"] diff --git a/tests/codegen-units/partitioning/local-drop-glue.rs b/tests/codegen-units/partitioning/local-drop-glue.rs index 5fa1df95cbc8..240f64e4f702 100644 --- a/tests/codegen-units/partitioning/local-drop-glue.rs +++ b/tests/codegen-units/partitioning/local-drop-glue.rs @@ -1,14 +1,14 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation -// We specify opt-level=0 because `drop_in_place` is `Internal` when optimizing //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus -Copt-level=0 +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -#![allow(dead_code)] #![crate_type = "rlib"] +// This test checks that drop glue is generated for types defined in this crate, and that all drop +// glue is put in the fallback CGU. +// This is rather similar to extern-drop-glue.rs. + //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(Struct)) @@ local_drop_glue-fallback.cgu[External] -struct Struct { +pub struct Struct { _a: u32, } @@ -18,7 +18,7 @@ impl Drop for Struct { } //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(Outer)) @@ local_drop_glue-fallback.cgu[External] -struct Outer { +pub struct Outer { _a: Struct, } @@ -33,7 +33,7 @@ pub mod mod1 { //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(mod1::Struct2)) @@ local_drop_glue-fallback.cgu[External] struct Struct2 { _a: Struct, - //~ MONO_ITEM fn std::ptr::drop_in_place::<(u32, Struct)> - shim(Some((u32, Struct))) @@ local_drop_glue-fallback.cgu[Internal] + //~ MONO_ITEM fn std::ptr::drop_in_place::<(u32, Struct)> - shim(Some((u32, Struct))) @@ local_drop_glue-fallback.cgu[External] _b: (u32, Struct), } diff --git a/tests/codegen-units/partitioning/local-generic.rs b/tests/codegen-units/partitioning/local-generic.rs index 0cfc572650c1..177eb2632f64 100644 --- a/tests/codegen-units/partitioning/local-generic.rs +++ b/tests/codegen-units/partitioning/local-generic.rs @@ -1,10 +1,11 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=eager +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -#![allow(dead_code)] #![crate_type = "lib"] +// This test checks that all the instantiations of a local generic fn are placed in the same CGU, +// regardless of where it is called. + //~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] //~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] //~ MONO_ITEM fn generic:: @@ local_generic.volatile[External] @@ -13,34 +14,34 @@ pub fn generic(x: T) -> T { x } -//~ MONO_ITEM fn user @@ local_generic[Internal] -fn user() { +//~ MONO_ITEM fn user @@ local_generic[External] +pub fn user() { let _ = generic(0u32); } -mod mod1 { +pub mod mod1 { pub use super::generic; - //~ MONO_ITEM fn mod1::user @@ local_generic-mod1[Internal] - fn user() { + //~ MONO_ITEM fn mod1::user @@ local_generic-mod1[External] + pub fn user() { let _ = generic(0u64); } - mod mod1 { + pub mod mod1 { use super::generic; - //~ MONO_ITEM fn mod1::mod1::user @@ local_generic-mod1-mod1[Internal] - fn user() { + //~ MONO_ITEM fn mod1::mod1::user @@ local_generic-mod1-mod1[External] + pub fn user() { let _ = generic('c'); } } } -mod mod2 { +pub mod mod2 { use super::generic; - //~ MONO_ITEM fn mod2::user @@ local_generic-mod2[Internal] - fn user() { + //~ MONO_ITEM fn mod2::user @@ local_generic-mod2[External] + pub fn user() { let _ = generic("abc"); } } diff --git a/tests/codegen-units/partitioning/local-inlining-but-not-all.rs b/tests/codegen-units/partitioning/local-inlining-but-not-all.rs deleted file mode 100644 index 454de255254a..000000000000 --- a/tests/codegen-units/partitioning/local-inlining-but-not-all.rs +++ /dev/null @@ -1,38 +0,0 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation -//@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus=no - -#![allow(dead_code)] -#![crate_type = "lib"] - -mod inline { - - //~ MONO_ITEM fn inline::inlined_function @@ local_inlining_but_not_all-inline[External] - #[inline] - pub fn inlined_function() {} -} - -pub mod user1 { - use super::inline; - - //~ MONO_ITEM fn user1::foo @@ local_inlining_but_not_all-user1[External] - pub fn foo() { - inline::inlined_function(); - } -} - -pub mod user2 { - use super::inline; - - //~ MONO_ITEM fn user2::bar @@ local_inlining_but_not_all-user2[External] - pub fn bar() { - inline::inlined_function(); - } -} - -pub mod non_user { - - //~ MONO_ITEM fn non_user::baz @@ local_inlining_but_not_all-non_user[External] - pub fn baz() {} -} diff --git a/tests/codegen-units/partitioning/local-inlining.rs b/tests/codegen-units/partitioning/local-inlining.rs deleted file mode 100644 index 42c68b5c6218..000000000000 --- a/tests/codegen-units/partitioning/local-inlining.rs +++ /dev/null @@ -1,39 +0,0 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation -//@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus - -#![allow(dead_code)] -#![crate_type = "lib"] - -mod inline { - - // Important: This function should show up in all codegen units where it is inlined - //~ MONO_ITEM fn inline::inlined_function @@ local_inlining-user1[Internal] local_inlining-user2[Internal] - #[inline(always)] - pub fn inlined_function() {} -} - -pub mod user1 { - use super::inline; - - //~ MONO_ITEM fn user1::foo @@ local_inlining-user1[External] - pub fn foo() { - inline::inlined_function(); - } -} - -pub mod user2 { - use super::inline; - - //~ MONO_ITEM fn user2::bar @@ local_inlining-user2[External] - pub fn bar() { - inline::inlined_function(); - } -} - -pub mod non_user { - - //~ MONO_ITEM fn non_user::baz @@ local_inlining-non_user[External] - pub fn baz() {} -} diff --git a/tests/codegen-units/partitioning/local-transitive-inlining.rs b/tests/codegen-units/partitioning/local-transitive-inlining.rs index 0d279ebe7404..bcd32bd2e264 100644 --- a/tests/codegen-units/partitioning/local-transitive-inlining.rs +++ b/tests/codegen-units/partitioning/local-transitive-inlining.rs @@ -1,11 +1,13 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -#![allow(dead_code)] #![crate_type = "rlib"] +// This test checks that a monomorphic inline(always) function is instantiated in every CGU that +// references it, even if it is only referenced via another module. +// The modules `inline` and `direct_user` do not get CGUs because they only define inline(always) +// functions, which always get lazy codegen. + mod inline { //~ MONO_ITEM fn inline::inlined_function @@ local_transitive_inlining-indirect_user[Internal] @@ -13,7 +15,7 @@ mod inline { pub fn inlined_function() {} } -mod direct_user { +pub mod direct_user { use super::inline; //~ MONO_ITEM fn direct_user::foo @@ local_transitive_inlining-indirect_user[Internal] diff --git a/tests/codegen-units/partitioning/methods-are-with-self-type.rs b/tests/codegen-units/partitioning/methods-are-with-self-type.rs index 94d06829c6c3..4d3f946fd953 100644 --- a/tests/codegen-units/partitioning/methods-are-with-self-type.rs +++ b/tests/codegen-units/partitioning/methods-are-with-self-type.rs @@ -1,9 +1,11 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 #![crate_type = "lib"] +// This test ensures that methods are assigned to the module where their self-type is defined, not +// where the method is defined. + pub struct SomeType; struct SomeGenericType(T1, T2); diff --git a/tests/codegen-units/partitioning/regular-modules.rs b/tests/codegen-units/partitioning/regular-modules.rs index 294971464155..d59074e7e347 100644 --- a/tests/codegen-units/partitioning/regular-modules.rs +++ b/tests/codegen-units/partitioning/regular-modules.rs @@ -1,71 +1,71 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=eager +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -#![allow(dead_code)] #![crate_type = "lib"] -//~ MONO_ITEM fn foo @@ regular_modules[Internal] -fn foo() {} +// This test ensures that regular fn items and statics are assigned to the CGU of their module. -//~ MONO_ITEM fn bar @@ regular_modules[Internal] -fn bar() {} +//~ MONO_ITEM fn foo @@ regular_modules[External] +pub fn foo() {} -//~ MONO_ITEM static BAZ @@ regular_modules[Internal] -static BAZ: u64 = 0; +//~ MONO_ITEM fn bar @@ regular_modules[External] +pub fn bar() {} -mod mod1 { +//~ MONO_ITEM static BAZ @@ regular_modules[External] +pub static BAZ: u64 = 0; - //~ MONO_ITEM fn mod1::foo @@ regular_modules-mod1[Internal] - fn foo() {} - //~ MONO_ITEM fn mod1::bar @@ regular_modules-mod1[Internal] - fn bar() {} - //~ MONO_ITEM static mod1::BAZ @@ regular_modules-mod1[Internal] - static BAZ: u64 = 0; +pub mod mod1 { - mod mod1 { - //~ MONO_ITEM fn mod1::mod1::foo @@ regular_modules-mod1-mod1[Internal] - fn foo() {} - //~ MONO_ITEM fn mod1::mod1::bar @@ regular_modules-mod1-mod1[Internal] - fn bar() {} - //~ MONO_ITEM static mod1::mod1::BAZ @@ regular_modules-mod1-mod1[Internal] - static BAZ: u64 = 0; + //~ MONO_ITEM fn mod1::foo @@ regular_modules-mod1[External] + pub fn foo() {} + //~ MONO_ITEM fn mod1::bar @@ regular_modules-mod1[External] + pub fn bar() {} + //~ MONO_ITEM static mod1::BAZ @@ regular_modules-mod1[External] + pub static BAZ: u64 = 0; + + pub mod mod1 { + //~ MONO_ITEM fn mod1::mod1::foo @@ regular_modules-mod1-mod1[External] + pub fn foo() {} + //~ MONO_ITEM fn mod1::mod1::bar @@ regular_modules-mod1-mod1[External] + pub fn bar() {} + //~ MONO_ITEM static mod1::mod1::BAZ @@ regular_modules-mod1-mod1[External] + pub static BAZ: u64 = 0; } - mod mod2 { - //~ MONO_ITEM fn mod1::mod2::foo @@ regular_modules-mod1-mod2[Internal] - fn foo() {} - //~ MONO_ITEM fn mod1::mod2::bar @@ regular_modules-mod1-mod2[Internal] - fn bar() {} - //~ MONO_ITEM static mod1::mod2::BAZ @@ regular_modules-mod1-mod2[Internal] - static BAZ: u64 = 0; + pub mod mod2 { + //~ MONO_ITEM fn mod1::mod2::foo @@ regular_modules-mod1-mod2[External] + pub fn foo() {} + //~ MONO_ITEM fn mod1::mod2::bar @@ regular_modules-mod1-mod2[External] + pub fn bar() {} + //~ MONO_ITEM static mod1::mod2::BAZ @@ regular_modules-mod1-mod2[External] + pub static BAZ: u64 = 0; } } -mod mod2 { +pub mod mod2 { - //~ MONO_ITEM fn mod2::foo @@ regular_modules-mod2[Internal] - fn foo() {} - //~ MONO_ITEM fn mod2::bar @@ regular_modules-mod2[Internal] - fn bar() {} - //~ MONO_ITEM static mod2::BAZ @@ regular_modules-mod2[Internal] - static BAZ: u64 = 0; + //~ MONO_ITEM fn mod2::foo @@ regular_modules-mod2[External] + pub fn foo() {} + //~ MONO_ITEM fn mod2::bar @@ regular_modules-mod2[External] + pub fn bar() {} + //~ MONO_ITEM static mod2::BAZ @@ regular_modules-mod2[External] + pub static BAZ: u64 = 0; - mod mod1 { - //~ MONO_ITEM fn mod2::mod1::foo @@ regular_modules-mod2-mod1[Internal] - fn foo() {} - //~ MONO_ITEM fn mod2::mod1::bar @@ regular_modules-mod2-mod1[Internal] - fn bar() {} - //~ MONO_ITEM static mod2::mod1::BAZ @@ regular_modules-mod2-mod1[Internal] - static BAZ: u64 = 0; + pub mod mod1 { + //~ MONO_ITEM fn mod2::mod1::foo @@ regular_modules-mod2-mod1[External] + pub fn foo() {} + //~ MONO_ITEM fn mod2::mod1::bar @@ regular_modules-mod2-mod1[External] + pub fn bar() {} + //~ MONO_ITEM static mod2::mod1::BAZ @@ regular_modules-mod2-mod1[External] + pub static BAZ: u64 = 0; } - mod mod2 { - //~ MONO_ITEM fn mod2::mod2::foo @@ regular_modules-mod2-mod2[Internal] - fn foo() {} - //~ MONO_ITEM fn mod2::mod2::bar @@ regular_modules-mod2-mod2[Internal] - fn bar() {} - //~ MONO_ITEM static mod2::mod2::BAZ @@ regular_modules-mod2-mod2[Internal] - static BAZ: u64 = 0; + pub mod mod2 { + //~ MONO_ITEM fn mod2::mod2::foo @@ regular_modules-mod2-mod2[External] + pub fn foo() {} + //~ MONO_ITEM fn mod2::mod2::bar @@ regular_modules-mod2-mod2[External] + pub fn bar() {} + //~ MONO_ITEM static mod2::mod2::BAZ @@ regular_modules-mod2-mod2[External] + pub static BAZ: u64 = 0; } } diff --git a/tests/codegen-units/partitioning/shared-generics.rs b/tests/codegen-units/partitioning/shared-generics.rs index ea312719ac91..b5bf376a6134 100644 --- a/tests/codegen-units/partitioning/shared-generics.rs +++ b/tests/codegen-units/partitioning/shared-generics.rs @@ -2,13 +2,19 @@ // NOTE: We always compile this test with -Copt-level=0 because higher opt-levels // prevent drop-glue from participating in share-generics. //@ incremental -//@ compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Copt-level=0 +//@ compile-flags: -Zprint-mono-items=lazy -Zshare-generics=yes -Copt-level=0 #![crate_type = "rlib"] //@ aux-build:shared_generics_aux.rs extern crate shared_generics_aux; +// This test ensures that when a crate and a dependency are compiled with -Zshare-generics, the +// downstream crate reuses generic instantiations from the dependency, but will still instantiate +// its own copy when instantiating with arguments that the dependency did not. +// Drop glue has a lot of special handling in the compiler, so we check that drop glue is also +// shared. + //~ MONO_ITEM fn foo pub fn foo() { //~ MONO_ITEM fn shared_generics_aux::generic_fn:: @@ shared_generics_aux-in-shared_generics.volatile[External] diff --git a/tests/codegen-units/partitioning/statics.rs b/tests/codegen-units/partitioning/statics.rs index 00dd6d877e1e..72bca4c5ed36 100644 --- a/tests/codegen-units/partitioning/statics.rs +++ b/tests/codegen-units/partitioning/statics.rs @@ -1,9 +1,11 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 #![crate_type = "rlib"] +// This test ensures that statics are assigned to the correct module when they are defined inside +// of a function. + //~ MONO_ITEM static FOO @@ statics[Internal] static FOO: u32 = 0; diff --git a/tests/codegen-units/partitioning/vtable-through-const.rs b/tests/codegen-units/partitioning/vtable-through-const.rs index 3880bba6f6b3..fd73e3fe9238 100644 --- a/tests/codegen-units/partitioning/vtable-through-const.rs +++ b/tests/codegen-units/partitioning/vtable-through-const.rs @@ -1,14 +1,11 @@ -// We specify incremental here because we want to test the partitioning for incremental compilation //@ incremental -//@ compile-flags:-Zprint-mono-items=lazy -//@ compile-flags:-Zinline-in-all-cgus // Need to disable optimizations to ensure consistent output across all CI runners. -//@ compile-flags:-Copt-level=0 +//@ compile-flags: -Zprint-mono-items=lazy -Copt-level=0 -// This test case makes sure, that references made through constants are -// recorded properly in the InliningMap. +#![crate_type = "rlib"] -#![crate_type = "lib"] +// This test case makes sure that references made through constants cause trait associated methods +// to be monomorphized when required. mod mod1 { struct NeedsDrop; @@ -43,7 +40,7 @@ mod mod1 { x } - // These are referenced, so they produce mono-items (see start()) + // These are referenced, so they produce mono-items (see main) pub const TRAIT1_REF: &'static Trait1 = &NeedsDrop as &Trait1; pub const TRAIT1_GEN_REF: &'static Trait1Gen = &NeedsDrop as &Trait1Gen; pub const ID_CHAR: fn(char) -> char = id::; @@ -77,9 +74,8 @@ mod mod1 { pub const ID_I64: fn(i64) -> i64 = id::; } -//~ MONO_ITEM fn start -#[no_mangle] -pub fn start(_: isize, _: *const *const u8) -> isize { +//~ MONO_ITEM fn main @@ vtable_through_const[External] +pub fn main() { //~ MONO_ITEM fn ::drop @@ vtable_through_const-fallback.cgu[External] //~ MONO_ITEM fn std::ptr::drop_in_place:: - shim(Some(mod1::NeedsDrop)) @@ vtable_through_const-fallback.cgu[External] @@ -103,6 +99,4 @@ pub fn start(_: isize, _: *const *const u8) -> isize { //~ MONO_ITEM fn mod1::id:: @@ vtable_through_const-mod1.volatile[External] mod1::ID_CHAR('x'); - - 0 } diff --git a/tests/run-make/sepcomp-cci-copies/cci_lib.rs b/tests/run-make/sepcomp-cci-copies/cci_lib.rs deleted file mode 100644 index 869d4a6cd3e4..000000000000 --- a/tests/run-make/sepcomp-cci-copies/cci_lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![crate_type = "rlib"] - -#[inline] -pub fn cci_fn() -> usize { - 1234 -} diff --git a/tests/run-make/sepcomp-cci-copies/foo.rs b/tests/run-make/sepcomp-cci-copies/foo.rs deleted file mode 100644 index ba251fcb0ac2..000000000000 --- a/tests/run-make/sepcomp-cci-copies/foo.rs +++ /dev/null @@ -1,25 +0,0 @@ -extern crate cci_lib; -use cci_lib::cci_fn; - -fn call1() -> usize { - cci_fn() -} - -mod a { - use cci_lib::cci_fn; - pub fn call2() -> usize { - cci_fn() - } -} - -mod b { - pub fn call3() -> usize { - 0 - } -} - -fn main() { - call1(); - a::call2(); - b::call3(); -} diff --git a/tests/run-make/sepcomp-cci-copies/rmake.rs b/tests/run-make/sepcomp-cci-copies/rmake.rs deleted file mode 100644 index a66cc2872b43..000000000000 --- a/tests/run-make/sepcomp-cci-copies/rmake.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Check that cross-crate inlined items are inlined in all compilation units -// that refer to them, and not in any other compilation units. -// Note that we have to pass `-C codegen-units=6` because up to two CGUs may be -// created for each source module (see `rustc_const_eval::monomorphize::partitioning`). -// See https://github.com/rust-lang/rust/pull/16367 - -use run_make_support::{count_regex_matches_in_files_with_extension, regex, rustc}; - -fn main() { - rustc().input("cci_lib.rs").run(); - rustc().input("foo.rs").emit("llvm-ir").codegen_units(6).arg("-Zinline-in-all-cgus").run(); - let re = regex::Regex::new(r#"define\ .*cci_fn"#).unwrap(); - assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2); -} diff --git a/tests/run-make/sepcomp-inlining/foo.rs b/tests/run-make/sepcomp-inlining/foo.rs deleted file mode 100644 index 9101ee691a4e..000000000000 --- a/tests/run-make/sepcomp-inlining/foo.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![crate_type = "lib"] - -#[inline] -fn inlined() -> u32 { - 1234 -} - -fn normal() -> u32 { - 2345 -} - -mod a { - pub fn f() -> u32 { - ::inlined() + ::normal() - } -} - -mod b { - pub fn f() -> u32 { - ::inlined() + ::normal() - } -} - -pub fn start(_: isize, _: *const *const u8) -> isize { - a::f(); - b::f(); - - 0 -} diff --git a/tests/run-make/sepcomp-inlining/rmake.rs b/tests/run-make/sepcomp-inlining/rmake.rs deleted file mode 100644 index ea4a4d210cc3..000000000000 --- a/tests/run-make/sepcomp-inlining/rmake.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that #[inline] functions still get inlined across compilation unit -// boundaries. Compilation should produce three IR files, but only the two -// compilation units that have a usage of the #[inline] function should -// contain a definition. Also, the non-#[inline] function should be defined -// in only one compilation unit. -// See https://github.com/rust-lang/rust/pull/16367 - -use run_make_support::{count_regex_matches_in_files_with_extension, regex, rustc}; - -fn main() { - rustc().input("foo.rs").emit("llvm-ir").codegen_units(3).arg("-Zinline-in-all-cgus").run(); - let re = regex::Regex::new(r#"define\ i32\ .*inlined"#).unwrap(); - assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 0); - let re = regex::Regex::new(r#"define\ internal\ .*inlined"#).unwrap(); - assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2); - let re = regex::Regex::new(r#"define\ hidden\ i32\ .*normal"#).unwrap(); - assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 1); - let re = regex::Regex::new(r#"declare\ hidden\ i32\ .*normal"#).unwrap(); - assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2); -} diff --git a/tests/run-make/sepcomp-separate/foo.rs b/tests/run-make/sepcomp-separate/foo.rs deleted file mode 100644 index 169bafa9b3a5..000000000000 --- a/tests/run-make/sepcomp-separate/foo.rs +++ /dev/null @@ -1,21 +0,0 @@ -fn magic_fn() -> usize { - 1234 -} - -mod a { - pub fn magic_fn() -> usize { - 2345 - } -} - -mod b { - pub fn magic_fn() -> usize { - 3456 - } -} - -fn main() { - magic_fn(); - a::magic_fn(); - b::magic_fn(); -} diff --git a/tests/run-make/sepcomp-separate/rmake.rs b/tests/run-make/sepcomp-separate/rmake.rs deleted file mode 100644 index 49958044a612..000000000000 --- a/tests/run-make/sepcomp-separate/rmake.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Test that separate compilation actually puts code into separate compilation -// units. `foo.rs` defines `magic_fn` in three different modules, which should -// wind up in three different compilation units. -// See https://github.com/rust-lang/rust/pull/16367 - -use run_make_support::{count_regex_matches_in_files_with_extension, regex, rustc}; - -fn main() { - rustc().input("foo.rs").emit("llvm-ir").codegen_units(3).run(); - let re = regex::Regex::new(r#"define\ .*magic_fn"#).unwrap(); - assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 3); -} From 4aaf467e2686750bd74f6011f51deca3bd797ce9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 25 Jan 2025 21:38:15 +0800 Subject: [PATCH 192/342] Implement MIR const trait stability checks --- compiler/rustc_const_eval/messages.ftl | 3 +- .../src/check_consts/check.rs | 168 +++++++++--------- .../rustc_const_eval/src/check_consts/mod.rs | 20 +-- .../rustc_const_eval/src/check_consts/ops.rs | 27 ++- compiler/rustc_const_eval/src/errors.rs | 12 +- .../const-traits/staged-api-user-crate.rs | 1 + .../const-traits/staged-api-user-crate.stderr | 13 +- tests/ui/traits/const-traits/staged-api.rs | 7 +- .../ui/traits/const-traits/staged-api.stderr | 101 ++++++++++- 9 files changed, 232 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 485c8696342a..d600d223bffd 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -403,7 +403,7 @@ const_eval_uninhabited_enum_variant_read = const_eval_uninhabited_enum_variant_written = writing discriminant of an uninhabited enum variant -const_eval_unmarked_const_fn_exposed = `{$def_path}` cannot be (indirectly) exposed to stable +const_eval_unmarked_const_item_exposed = `{$def_path}` cannot be (indirectly) exposed to stable .help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval) @@ -414,6 +414,7 @@ const_eval_unreachable_unwind = const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn +const_eval_unstable_const_trait = `{$def_path}` is not yet stable as a const trait const_eval_unstable_in_stable_exposed = const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ed34996a7a7d..b9e7c1ebb44d 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -8,6 +8,7 @@ use std::ops::Deref; use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_index::bit_set::DenseBitSet; @@ -29,7 +30,7 @@ use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; -use crate::check_consts::is_safe_to_expose_on_stable_const_fn; +use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable; use crate::errors; type QualifResults<'mir, 'tcx, Q> = @@ -470,6 +471,88 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id)) }) } + + /// Check the const stability of the given item (fn or trait). + fn check_callee_stability(&mut self, def_id: DefId) { + match self.tcx.lookup_const_stability(def_id) { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + // All good. + } + None => { + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. + if self.enforce_recursive_const_stability() + && !is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id) + { + self.dcx().emit_err(errors::UnmarkedConstItemExposed { + span: self.span, + def_path: self.tcx.def_path_str(def_id), + }); + } + } + Some(ConstStability { + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + feature, + .. + }) => { + // An unstable const fn/trait with a feature gate. + let callee_safe_to_expose_on_stable = + is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id); + + // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if + // the callee is safe to expose, to avoid bypassing recursive stability. + // This is not ideal since it means the user sees an error, not the macro + // author, but that's also the case if one forgets to set + // `#[allow_internal_unstable]` in the first place. Note that this cannot be + // integrated in the check below since we want to enforce + // `callee_safe_to_expose_on_stable` even if + // `!self.enforce_recursive_const_stability()`. + if (self.span.allows_unstable(feature) + || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) + && callee_safe_to_expose_on_stable + { + return; + } + + // We can't use `check_op` to check whether the feature is enabled because + // the logic is a bit different than elsewhere: local functions don't need + // the feature gate, and there might be an "implied" gate that also suffices + // to allow this. + let feature_enabled = def_id.is_local() + || self.tcx.features().enabled(feature) + || implied_feature.is_some_and(|f| self.tcx.features().enabled(f)) + || { + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + // This matches what we do in `eval_stability_allow_unstable` for + // regular stability. + feature == sym::rustc_private + && issue == NonZero::new(27812) + && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + }; + // Even if the feature is enabled, we still need check_op to double-check + // this if the callee is not safe to expose on stable. + if !feature_enabled || !callee_safe_to_expose_on_stable { + self.check_op(ops::CallUnstable { + def_id, + feature, + feature_enabled, + safe_to_expose_on_stable: callee_safe_to_expose_on_stable, + suggestion_span: self.crate_inject_span(), + is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait, + }); + } + } + } + } } impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { @@ -716,8 +799,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { span: *fn_span, call_source, }); - // FIXME(const_trait_impl): do a more fine-grained check whether this - // particular trait can be const-stably called. + self.check_callee_stability(trait_did); } else { // Not even a const trait. self.check_op(ops::FnCallNonConst { @@ -793,7 +875,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // fallback body is safe to expose on stable. let is_const_stable = intrinsic.const_stable || (!intrinsic.must_be_overridden - && is_safe_to_expose_on_stable_const_fn(tcx, callee)); + && is_fn_or_trait_safe_to_expose_on_stable(tcx, callee)); match tcx.lookup_const_stability(callee) { None => { // This doesn't need a separate const-stability check -- const-stability equals @@ -842,83 +924,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // Finally, stability for regular function calls -- this is the big one. - match tcx.lookup_const_stability(callee) { - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { - // All good. - } - None => { - // This doesn't need a separate const-stability check -- const-stability equals - // regular stability, and regular stability is checked separately. - // However, we *do* have to worry about *recursive* const stability. - if self.enforce_recursive_const_stability() - && !is_safe_to_expose_on_stable_const_fn(tcx, callee) - { - self.dcx().emit_err(errors::UnmarkedConstFnExposed { - span: self.span, - def_path: self.tcx.def_path_str(callee), - }); - } - } - Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, - feature, - .. - }) => { - // An unstable const fn with a feature gate. - let callee_safe_to_expose_on_stable = - is_safe_to_expose_on_stable_const_fn(tcx, callee); - - // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if - // the callee is safe to expose, to avoid bypassing recursive stability. - // This is not ideal since it means the user sees an error, not the macro - // author, but that's also the case if one forgets to set - // `#[allow_internal_unstable]` in the first place. Note that this cannot be - // integrated in the check below since we want to enforce - // `callee_safe_to_expose_on_stable` even if - // `!self.enforce_recursive_const_stability()`. - if (self.span.allows_unstable(feature) - || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) - && callee_safe_to_expose_on_stable - { - return; - } - - // We can't use `check_op` to check whether the feature is enabled because - // the logic is a bit different than elsewhere: local functions don't need - // the feature gate, and there might be an "implied" gate that also suffices - // to allow this. - let feature_enabled = callee.is_local() - || tcx.features().enabled(feature) - || implied_feature.is_some_and(|f| tcx.features().enabled(f)) - || { - // When we're compiling the compiler itself we may pull in - // crates from crates.io, but those crates may depend on other - // crates also pulled in from crates.io. We want to ideally be - // able to compile everything without requiring upstream - // modifications, so in the case that this looks like a - // `rustc_private` crate (e.g., a compiler crate) and we also have - // the `-Z force-unstable-if-unmarked` flag present (we're - // compiling a compiler crate), then let this missing feature - // annotation slide. - // This matches what we do in `eval_stability_allow_unstable` for - // regular stability. - feature == sym::rustc_private - && issue == NonZero::new(27812) - && tcx.sess.opts.unstable_opts.force_unstable_if_unmarked - }; - // Even if the feature is enabled, we still need check_op to double-check - // this if the callee is not safe to expose on stable. - if !feature_enabled || !callee_safe_to_expose_on_stable { - self.check_op(ops::FnCallUnstable { - def_id: callee, - feature, - feature_enabled, - safe_to_expose_on_stable: callee_safe_to_expose_on_stable, - suggestion_span: self.crate_inject_span(), - }); - } - } - } + self.check_callee_stability(callee); } // Forbid all `Drop` terminators unless the place being dropped is a local with no diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index ab68691f1b97..bfa0a0319c34 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -56,7 +56,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { self.const_kind == Some(hir::ConstContext::ConstFn) && (self.tcx.features().staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) - && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) + && is_fn_or_trait_safe_to_expose_on_stable(self.tcx, self.def_id().to_def_id()) } fn is_async(&self) -> bool { @@ -84,28 +84,14 @@ pub fn rustc_allow_const_fn_unstable( attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) } -/// Returns `true` if the given `const fn` is "safe to expose on stable". -/// -/// Panics if the given `DefId` does not refer to a `const fn`. +/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". /// /// This is relevant within a `staged_api` crate. Unlike with normal features, the use of unstable /// const features *recursively* taints the functions that use them. This is to avoid accidentally /// exposing e.g. the implementation of an unstable const intrinsic on stable. So we partition the /// world into two functions: those that are safe to expose on stable (and hence may not use /// unstable features, not even recursively), and those that are not. -pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // A default body in a `#[const_trait]` is not const-stable because const trait fns currently - // cannot be const-stable. These functions can't be called from anything stable, so we shouldn't - // restrict them to only call const-stable functions. - if tcx.is_const_default_method(def_id) { - // FIXME(const_trait_impl): we have to eventually allow some of these if these things can ever be stable. - // They should probably behave like regular `const fn` for that... - return false; - } - - // Const-stability is only relevant for `const fn`. - assert!(tcx.is_const_fn(def_id)); - +pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { match tcx.lookup_const_stability(def_id) { None => { // In a `staged_api` crate, we do enforce recursive const stability for all unmarked diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 3c83a7b92cdc..7756e51c4c5f 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -377,11 +377,11 @@ fn build_error_for_const_call<'tcx>( err } -/// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function. +/// A call to an `#[unstable]` const fn, `#[rustc_const_unstable]` function or trait. /// -/// Contains the name of the feature that would allow the use of this function. +/// Contains the name of the feature that would allow the use of this function/trait. #[derive(Debug)] -pub(crate) struct FnCallUnstable { +pub(crate) struct CallUnstable { pub def_id: DefId, pub feature: Symbol, /// If this is true, then the feature is enabled, but we need to still check if it is safe to @@ -389,24 +389,33 @@ pub(crate) struct FnCallUnstable { pub feature_enabled: bool, pub safe_to_expose_on_stable: bool, pub suggestion_span: Option, + /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait. + pub is_function_call: bool, } -impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { +impl<'tcx> NonConstOp<'tcx> for CallUnstable { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable { gate: self.feature, gate_already_checked: self.feature_enabled, safe_to_expose_on_stable: self.safe_to_expose_on_stable, - is_function_call: true, + is_function_call: self.is_function_call, } } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { assert!(!self.feature_enabled); - let mut err = ccx.dcx().create_err(errors::UnstableConstFn { - span, - def_path: ccx.tcx.def_path_str(self.def_id), - }); + let mut err = if self.is_function_call { + ccx.dcx().create_err(errors::UnstableConstFn { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + } else { + ccx.dcx().create_err(errors::UnstableConstTrait { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + }; // FIXME: make this translatable let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature); #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 1ee9214c4b2a..a2635885098e 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -121,6 +121,14 @@ pub(crate) struct UnstableConstFn { pub def_path: String, } +#[derive(Diagnostic)] +#[diag(const_eval_unstable_const_trait)] +pub(crate) struct UnstableConstTrait { + #[primary_span] + pub span: Span, + pub def_path: String, +} + #[derive(Diagnostic)] #[diag(const_eval_unstable_intrinsic)] pub(crate) struct UnstableIntrinsic { @@ -139,9 +147,9 @@ pub(crate) struct UnstableIntrinsic { } #[derive(Diagnostic)] -#[diag(const_eval_unmarked_const_fn_exposed)] +#[diag(const_eval_unmarked_const_item_exposed)] #[help] -pub(crate) struct UnmarkedConstFnExposed { +pub(crate) struct UnmarkedConstItemExposed { #[primary_span] pub span: Span, pub def_path: String, diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.rs b/tests/ui/traits/const-traits/staged-api-user-crate.rs index 7587042cf276..4aa75a50355b 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.rs +++ b/tests/ui/traits/const-traits/staged-api-user-crate.rs @@ -11,6 +11,7 @@ fn non_const_context() { const fn stable_const_context() { Unstable::func(); //~^ ERROR cannot call conditionally-const associated function `::func` in constant functions + //~| ERROR `staged_api::MyTrait` is not yet stable as a const trait } fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.stderr b/tests/ui/traits/const-traits/staged-api-user-crate.stderr index 400c76fcaf49..8ac83770cf7a 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.stderr +++ b/tests/ui/traits/const-traits/staged-api-user-crate.stderr @@ -9,6 +9,17 @@ LL | Unstable::func(); = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: `staged_api::MyTrait` is not yet stable as a const trait + --> $DIR/staged-api-user-crate.rs:12:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: add `#![feature(unstable)]` to the crate attributes to enable + | +LL + #![feature(unstable)] + | + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 755a4e456bcf..406aaab54716 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -22,7 +22,7 @@ impl const MyTrait for Foo { fn func() {} } -#[rustc_allow_const_fn_unstable(const_trait_impl)] +#[rustc_allow_const_fn_unstable(const_trait_impl, unstable)] const fn conditionally_const() { T::func(); } @@ -37,10 +37,13 @@ fn non_const_context() { const fn const_context() { Unstable::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Foo::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Unstable2::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` conditionally_const::(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` } @@ -59,8 +62,10 @@ pub const fn const_context_not_const_stable() { const fn stable_const_context() { Unstable::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Foo::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` const_context_not_const_stable(); //~^ ERROR cannot use `#[feature(local_feature)]` conditionally_const::(); diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index acc93f747a8e..5e206d579349 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -15,8 +15,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:38:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:40:5 + --> $DIR/staged-api.rs:41:5 | LL | Foo::func(); | ^^^^^^^^^^^ @@ -32,8 +49,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:41:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:42:5 + --> $DIR/staged-api.rs:44:5 | LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ @@ -49,9 +83,26 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` --> $DIR/staged-api.rs:44:5 | +LL | Unstable2::func(); + | ^^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:47:5 + | LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -67,7 +118,7 @@ LL | const fn const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:60:5 + --> $DIR/staged-api.rs:63:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ @@ -83,8 +134,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:63:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn stable_const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:62:5 + --> $DIR/staged-api.rs:66:5 | LL | Foo::func(); | ^^^^^^^^^^^ @@ -100,8 +168,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:66:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn stable_const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` - --> $DIR/staged-api.rs:64:5 + --> $DIR/staged-api.rs:69:5 | LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -119,7 +204,7 @@ LL | const fn stable_const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:66:5 + --> $DIR/staged-api.rs:71:5 | LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,5 +220,5 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | -error: aborting due to 8 previous errors +error: aborting due to 13 previous errors From d250657a3c398e80e4d53bd311cafce82512b87a Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 28 Jan 2025 04:33:39 +0000 Subject: [PATCH 193/342] add test case for implicitly stable const fn --- tests/ui/traits/const-traits/staged-api.rs | 13 +++ .../ui/traits/const-traits/staged-api.stderr | 105 +++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 406aaab54716..9a030dafd6bc 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -72,4 +72,17 @@ const fn stable_const_context() { //~^ ERROR cannot use `#[feature(const_trait_impl)]` } +const fn implicitly_stable_const_context() { + Unstable::func(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` + Foo::func(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` + const_context_not_const_stable(); + //~^ ERROR cannot use `#[feature(local_feature)]` + conditionally_const::(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` +} + fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index 5e206d579349..a7a7a1ee7215 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -220,5 +220,108 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | -error: aborting due to 13 previous errors +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:76:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:76:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:79:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:79:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` + --> $DIR/staged-api.rs:82:5 + | +LL | const_context_not_const_stable(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features +help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(local_feature)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:84:5 + | +LL | conditionally_const::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: aborting due to 19 previous errors From 7f83f8ae727adb4893a83abea9d5dc0ea5a9b149 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 28 Jan 2025 14:11:33 +0900 Subject: [PATCH 194/342] Reject unsound toggling of Arm atomics-32 target feature --- compiler/rustc_target/src/target_features.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa..46e29304502b 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -137,6 +137,11 @@ const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("aclass", Unstable(sym::arm_target_feature), &[]), ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ( + "atomics-32", + Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + &[], + ), ("crc", Unstable(sym::arm_target_feature), &[]), ("d32", Unstable(sym::arm_target_feature), &[]), ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), From be7d6e3d5e34641f99f4a77d143beefbf243a934 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 21 Jan 2025 08:17:59 -0800 Subject: [PATCH 195/342] add work-in-progress unstable book chapters These are very bare-bones, only intended to provide some documentation of what these feature gates are and aren't yet implementing. --- .../ref-pat-eat-one-layer-2024-structural.md | 19 +++++++++++++++++++ .../ref-pat-eat-one-layer-2024.md | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md create mode 100644 src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md diff --git a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md new file mode 100644 index 000000000000..bc5876861117 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md @@ -0,0 +1,19 @@ +# `ref_pat_eat_one_layer_2024_structural` + +The tracking issue for this feature is: [#123076] + +[#123076]: https://github.com/rust-lang/rust/issues/123076 + +--- + +This feature is incomplete and not yet intended for general use. + +This implements experimental, Edition-dependent match ergonomics under consideration for inclusion +in Rust. +For more information, see the corresponding typing rules for [Editions 2024 and later]. +On earlier Editions, the current behavior is unspecified. + +For alternative experimental match ergonomics, see the feature +[`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md). + +[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAgEBAQEBAgIAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false diff --git a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md new file mode 100644 index 000000000000..43de1849a5eb --- /dev/null +++ b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md @@ -0,0 +1,19 @@ +# `ref_pat_eat_one_layer_2024` + +The tracking issue for this feature is: [#123076] + +[#123076]: https://github.com/rust-lang/rust/issues/123076 + +--- + +This feature is incomplete and not yet intended for general use. + +This implements experimental, Edition-dependent match ergonomics under consideration for inclusion +in Rust. +For more information, see the corresponding typing rules for [Editions 2024 and later]. +On earlier Editions, the current behavior is unspecified. + +For alternative experimental match ergonomics, see the feature +[`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md). + +[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAAABAQABAgIAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false From ea70688f7d2cbe567570902e56ecb4e08e7c8add Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 28 Jan 2025 14:41:15 +0900 Subject: [PATCH 196/342] Update comments and sort target_arch in c_char_definition --- library/core/src/ffi/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 79d094556c45..51687a3adcdd 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -116,7 +116,6 @@ mod c_char_definition { // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary // Interface says "The char type is unsigned by default". // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf - // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). // powerpc/powerpc64: // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC // Processor Supplement says ANSI C char is unsigned byte @@ -139,8 +138,10 @@ mod c_char_definition { // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types - // Xtensa: - // - "The char type is unsigned by default for Xtensa processors." + // xtensa: + // Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook + // says "`char` type is unsigned by default". + // https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf // // On the following operating systems, c_char is signed by default, regardless of architecture. // Darwin (macOS, iOS, etc.): @@ -150,11 +151,12 @@ mod c_char_definition { // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char // are promoted to int as if from type signed char by default, unless the /J compilation // option is used." - // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types) - // L4RE: + // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types + // L4Re: // The kernel builds with -funsigned-char on all targets (but useserspace follows the // architecture defaults). As we only have a target for userspace apps so there are no - // special cases for L4RE below. + // special cases for L4Re below. + // https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240 if #[cfg(all( not(windows), not(target_vendor = "apple"), @@ -166,8 +168,8 @@ mod c_char_definition { target_arch = "msp430", target_arch = "powerpc", target_arch = "powerpc64", - target_arch = "riscv64", target_arch = "riscv32", + target_arch = "riscv64", target_arch = "s390x", target_arch = "xtensa", ) From 4693d0a9ffceeabed36d2e82be3cfce5e1322b30 Mon Sep 17 00:00:00 2001 From: Yutaro Ohno Date: Tue, 14 Jan 2025 17:06:31 +0900 Subject: [PATCH 197/342] Add new lint `doc_overindented_list_items` Add a new lint `doc_overindented_list_items` to detect and fix list items in docs that are overindented. For example, ```rs /// - first line /// second line fn foo() {} ``` this would be fixed to: ```rs /// - first line /// second line fn foo() {} ``` This lint improves readabiliy and consistency in doc. --- CHANGELOG.md | 1 + README.md | 6 +- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc/lazy_continuation.rs | 108 ++++++++++++------ clippy_lints/src/doc/mod.rs | 34 ++++++ tests/ui/doc/doc_lazy_list.fixed | 1 + tests/ui/doc/doc_lazy_list.rs | 1 + tests/ui/doc/doc_lazy_list.stderr | 22 ++-- .../ui/doc/doc_overindented_list_items.fixed | 28 +++++ tests/ui/doc/doc_overindented_list_items.rs | 28 +++++ .../ui/doc/doc_overindented_list_items.stderr | 41 +++++++ 11 files changed, 219 insertions(+), 52 deletions(-) create mode 100644 tests/ui/doc/doc_overindented_list_items.fixed create mode 100644 tests/ui/doc/doc_overindented_list_items.rs create mode 100644 tests/ui/doc/doc_overindented_list_items.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2757597fc511..bc42c07224e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5533,6 +5533,7 @@ Released 2018-09-13 [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs +[`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use diff --git a/README.md b/README.md index cb3a22d4288f..32c1d33e2ed3 100644 --- a/README.md +++ b/README.md @@ -159,11 +159,11 @@ line. (You can swap `clippy::all` with the specific lint category you are target You can add options to your code to `allow`/`warn`/`deny` Clippy lints: * the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`). - Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). + Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). * all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, - `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive - lints prone to false positives. + `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive + lints prone to false positives. * only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ec223381aec6..da73ef3ced4c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -142,6 +142,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::DOC_NESTED_REFDEFS_INFO, + crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO, crate::doc::EMPTY_DOCS_INFO, crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs index d6ab9ac6749b..2577324f23df 100644 --- a/clippy_lints/src/doc/lazy_continuation.rs +++ b/clippy_lints/src/doc/lazy_continuation.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_span::{BytePos, Span}; +use std::cmp::Ordering; use std::ops::Range; -use super::DOC_LAZY_CONTINUATION; +use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS}; fn map_container_to_text(c: &super::Container) -> &'static str { match c { @@ -28,12 +29,57 @@ pub(super) fn check( return; } + // Blockquote let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); let blockquote_level = containers .iter() .filter(|c| matches!(c, super::Container::Blockquote)) .count(); - let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + if ccount < blockquote_level { + span_lint_and_then( + cx, + DOC_LAZY_CONTINUATION, + span, + "doc quote line without `>` marker", + |diag| { + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span.with_lo( + span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")), + ); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_verbose( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + ); + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + }, + ); + return; + } + + if ccount != 0 && blockquote_level != 0 { + // If this doc is a blockquote, we don't go further. + return; + } + + // List + let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count(); let list_indentation = containers .iter() .map(|c| { @@ -44,16 +90,15 @@ pub(super) fn check( } }) .sum(); - if ccount < blockquote_level || lcount < list_indentation { - let msg = if ccount < blockquote_level { - "doc quote line without `>` marker" - } else { - "doc list item without indentation" - }; - span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { - if ccount == 0 && blockquote_level == 0 { + match leading_spaces.cmp(&list_indentation) { + Ordering::Less => span_lint_and_then( + cx, + DOC_LAZY_CONTINUATION, + span, + "doc list item without indentation", + |diag| { // simpler suggestion style for indentation - let indent = list_indentation - lcount; + let indent = list_indentation - leading_spaces; diag.span_suggestion_verbose( span.shrink_to_hi(), "indent this line", @@ -61,33 +106,20 @@ pub(super) fn check( Applicability::MaybeIncorrect, ); diag.help("if this is supposed to be its own paragraph, add a blank line"); - return; - } - let mut doc_start_range = &doc[range]; - let mut suggested = String::new(); - for c in containers { - let text = map_container_to_text(c); - if doc_start_range.starts_with(text) { - doc_start_range = &doc_start_range[text.len()..]; - span = span - .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); - } else if matches!(c, super::Container::Blockquote) - && let Some(i) = doc_start_range.find('>') - { - doc_start_range = &doc_start_range[i + 1..]; - span = - span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); - } else { - suggested.push_str(text); - } - } - diag.span_suggestion_verbose( + }, + ), + Ordering::Greater => { + let sugg = std::iter::repeat_n(" ", list_indentation).join(""); + span_lint_and_sugg( + cx, + DOC_OVERINDENTED_LIST_ITEMS, span, - "add markers to start of line", - suggested, - Applicability::MachineApplicable, + "doc list item overindented", + format!("try using `{sugg}` ({list_indentation} spaces)"), + sugg, + Applicability::MaybeIncorrect, ); - diag.help("if this not intended to be a quote at all, escape it with `\\>`"); - }); + }, + Ordering::Equal => {}, } } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 7561a6cf2a78..15530c3dbc50 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -428,6 +428,39 @@ declare_clippy_lint! { "require every line of a paragraph to be indented and marked" } +declare_clippy_lint! { + /// ### What it does + /// + /// Detects overindented list items in doc comments where the continuation + /// lines are indented more than necessary. + /// + /// ### Why is this bad? + /// + /// Overindented list items in doc comments can lead to inconsistent and + /// poorly formatted documentation when rendered. Excessive indentation may + /// cause the text to be misinterpreted as a nested list item or code block, + /// affecting readability and the overall structure of the documentation. + /// + /// ### Example + /// + /// ```no_run + /// /// - This is the first item in a list + /// /// and this line is overindented. + /// # fn foo() {} + /// ``` + /// + /// Fixes this into: + /// ```no_run + /// /// - This is the first item in a list + /// /// and this line is overindented. + /// # fn foo() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_OVERINDENTED_LIST_ITEMS, + style, + "ensure list items are not overindented" +} + declare_clippy_lint! { /// ### What it does /// Checks if the first paragraph in the documentation of items listed in the module page is too long. @@ -617,6 +650,7 @@ impl_lint_pass!(Documentation => [ SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, DOC_LAZY_CONTINUATION, + DOC_OVERINDENTED_LIST_ITEMS, EMPTY_LINE_AFTER_OUTER_ATTR, EMPTY_LINE_AFTER_DOC_COMMENTS, TOO_LONG_FIRST_DOC_PARAGRAPH, diff --git a/tests/ui/doc/doc_lazy_list.fixed b/tests/ui/doc/doc_lazy_list.fixed index 0822cc7c6350..8e2ed1bbd18d 100644 --- a/tests/ui/doc/doc_lazy_list.fixed +++ b/tests/ui/doc/doc_lazy_list.fixed @@ -1,4 +1,5 @@ #![warn(clippy::doc_lazy_continuation)] +#![allow(clippy::doc_overindented_list_items)] /// 1. nest here /// lazy continuation diff --git a/tests/ui/doc/doc_lazy_list.rs b/tests/ui/doc/doc_lazy_list.rs index 068de140e00a..1da11d8fae26 100644 --- a/tests/ui/doc/doc_lazy_list.rs +++ b/tests/ui/doc/doc_lazy_list.rs @@ -1,4 +1,5 @@ #![warn(clippy::doc_lazy_continuation)] +#![allow(clippy::doc_overindented_list_items)] /// 1. nest here /// lazy continuation diff --git a/tests/ui/doc/doc_lazy_list.stderr b/tests/ui/doc/doc_lazy_list.stderr index b38f43b7555f..cea6157119f7 100644 --- a/tests/ui/doc/doc_lazy_list.stderr +++ b/tests/ui/doc/doc_lazy_list.stderr @@ -1,5 +1,5 @@ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:4:5 + --> tests/ui/doc/doc_lazy_list.rs:5:5 | LL | /// lazy continuation | ^ @@ -13,7 +13,7 @@ LL | /// lazy continuation | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:9:5 + --> tests/ui/doc/doc_lazy_list.rs:10:5 | LL | /// lazy list continuations don't make warnings with this lint | ^ @@ -25,7 +25,7 @@ LL | /// lazy list continuations don't make warnings with this lint | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:11:5 + --> tests/ui/doc/doc_lazy_list.rs:12:5 | LL | /// because they don't have the | ^ @@ -37,7 +37,7 @@ LL | /// because they don't have the | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:16:5 + --> tests/ui/doc/doc_lazy_list.rs:17:5 | LL | /// lazy continuation | ^ @@ -49,7 +49,7 @@ LL | /// lazy continuation | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:21:5 + --> tests/ui/doc/doc_lazy_list.rs:22:5 | LL | /// lazy list continuations don't make warnings with this lint | ^ @@ -61,7 +61,7 @@ LL | /// lazy list continuations don't make warnings with this lint | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:23:5 + --> tests/ui/doc/doc_lazy_list.rs:24:5 | LL | /// because they don't have the | ^ @@ -73,7 +73,7 @@ LL | /// because they don't have the | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:28:5 + --> tests/ui/doc/doc_lazy_list.rs:29:5 | LL | /// lazy continuation | ^ @@ -85,7 +85,7 @@ LL | /// lazy continuation | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:33:5 + --> tests/ui/doc/doc_lazy_list.rs:34:5 | LL | /// this will warn on the lazy continuation | ^ @@ -97,7 +97,7 @@ LL | /// this will warn on the lazy continuation | ++++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:35:5 + --> tests/ui/doc/doc_lazy_list.rs:36:5 | LL | /// and so should this | ^^^^ @@ -109,7 +109,7 @@ LL | /// and so should this | ++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:56:5 + --> tests/ui/doc/doc_lazy_list.rs:57:5 | LL | /// 'protocol_descriptors': [ | ^ @@ -121,7 +121,7 @@ LL | /// 'protocol_descriptors': [ | + error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:75:5 + --> tests/ui/doc/doc_lazy_list.rs:76:5 | LL | /// ] | ^ diff --git a/tests/ui/doc/doc_overindented_list_items.fixed b/tests/ui/doc/doc_overindented_list_items.fixed new file mode 100644 index 000000000000..940cff48c1e9 --- /dev/null +++ b/tests/ui/doc/doc_overindented_list_items.fixed @@ -0,0 +1,28 @@ +#![warn(clippy::doc_overindented_list_items)] + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn foo() {} + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn bar() {} + +#[rustfmt::skip] +/// * first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// * second list item +fn baz() {} diff --git a/tests/ui/doc/doc_overindented_list_items.rs b/tests/ui/doc/doc_overindented_list_items.rs new file mode 100644 index 000000000000..77f3ee8a64d4 --- /dev/null +++ b/tests/ui/doc/doc_overindented_list_items.rs @@ -0,0 +1,28 @@ +#![warn(clippy::doc_overindented_list_items)] + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn foo() {} + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn bar() {} + +#[rustfmt::skip] +/// * first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// * second list item +fn baz() {} diff --git a/tests/ui/doc/doc_overindented_list_items.stderr b/tests/ui/doc/doc_overindented_list_items.stderr new file mode 100644 index 000000000000..ff201ba5eb94 --- /dev/null +++ b/tests/ui/doc/doc_overindented_list_items.stderr @@ -0,0 +1,41 @@ +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:5:5 + | +LL | /// overindented line + | ^^^^^^^ help: try using ` ` (2 spaces) + | + = note: `-D clippy::doc-overindented-list-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_overindented_list_items)]` + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:7:5 + | +LL | /// this is overindented line too + | ^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:14:7 + | +LL | /// overindented line + | ^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:16:7 + | +LL | /// this is overindented line too + | ^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:23:5 + | +LL | /// overindented line + | ^^^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:25:5 + | +LL | /// this is overindented line too + | ^^^^^ help: try using ` ` (2 spaces) + +error: aborting due to 6 previous errors + From 3d6c6fa04e51219d0937df651d947f60d03b2772 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Sun, 26 Jan 2025 13:22:45 +0100 Subject: [PATCH 198/342] Document powf and powi calls that always return 1.0 --- library/std/src/f128.rs | 18 +++++++++++++++++- library/std/src/f16.rs | 18 +++++++++++++++++- library/std/src/f32.rs | 7 +++++-- library/std/src/f64.rs | 7 +++++-- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 4f37e18a8cd7..d65f5ed61cfb 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -324,6 +324,20 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -347,8 +361,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 42cd6e3fe2a5..5b0903bceabb 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -324,6 +324,20 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -347,8 +361,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 438d77b1626b..f9b6723788ae 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -306,8 +306,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +330,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 9bb4bfbab2a0..0de55a15d48e 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -306,8 +306,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +330,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] From 7877d86163d802b2963d893850555d684c9d0eb1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jan 2025 08:08:46 +0000 Subject: [PATCH 199/342] Add mir-opt pattern type tests --- .../pattern_types.main.PreCodegen.after.mir | 15 +++++++++++++++ tests/mir-opt/pattern_types.rs | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/mir-opt/pattern_types.main.PreCodegen.after.mir create mode 100644 tests/mir-opt/pattern_types.rs diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir new file mode 100644 index 000000000000..e96526a01ff8 --- /dev/null +++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir @@ -0,0 +1,15 @@ +// MIR for `main` after PreCodegen + +fn main() -> () { + let mut _0: (); + scope 1 { + debug x => const {transmute(0x00000002): (u32) is 1..=}; + scope 2 { + debug y => const {transmute(0x00000000): (u32) is 1..=}; + } + } + + bb0: { + return; + } +} diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs new file mode 100644 index 000000000000..3903fbad4ae8 --- /dev/null +++ b/tests/mir-opt/pattern_types.rs @@ -0,0 +1,12 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +// EMIT_MIR pattern_types.main.PreCodegen.after.mir +fn main() { + // CHECK: debug x => const {transmute(0x00000002): (u32) is 1..=} + let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) }; + // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=} + let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; +} From fd6713fce1d06ba283bc27331df354b7a9443d6b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 15:19:49 +0000 Subject: [PATCH 200/342] Make mir dumps more readable --- compiler/rustc_middle/src/ty/print/pretty.rs | 4 ++++ tests/mir-opt/pattern_types.main.PreCodegen.after.mir | 4 ++-- tests/mir-opt/pattern_types.rs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ac900edefe12..027a4315b4bf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1740,6 +1740,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { " as ", )?; } + ty::Pat(base_ty, pat) => { + self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?; + p!(write(" is {pat:?}")); + } // Nontrivial types with scalar bit representation _ => { let print = |this: &mut Self| { diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir index e96526a01ff8..8c99902f9b8f 100644 --- a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir +++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir @@ -3,9 +3,9 @@ fn main() -> () { let mut _0: (); scope 1 { - debug x => const {transmute(0x00000002): (u32) is 1..=}; + debug x => const 2_u32 is 1..=; scope 2 { - debug y => const {transmute(0x00000000): (u32) is 1..=}; + debug y => const 0_u32 is 1..=; } } diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs index 3903fbad4ae8..217c64b90cbb 100644 --- a/tests/mir-opt/pattern_types.rs +++ b/tests/mir-opt/pattern_types.rs @@ -5,8 +5,8 @@ use std::pat::pattern_type; // EMIT_MIR pattern_types.main.PreCodegen.after.mir fn main() { - // CHECK: debug x => const {transmute(0x00000002): (u32) is 1..=} + // CHECK: debug x => const 2_u32 is 1..= let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) }; - // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=} + // CHECK: debug y => const 0_u32 is 1..= let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; } From d62f885a8e055e977a5c0afdcca59376a388f8b2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 10:37:16 +0000 Subject: [PATCH 201/342] Edit the inputs to const == val check instead of duplicating logic --- .../src/builder/matches/test.rs | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index f7b0f734b2de..afe6b4475be3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -141,47 +141,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, self.source_info(match_start_span), terminator); } - TestKind::Eq { value, ty } => { + TestKind::Eq { value, mut ty } => { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); let expect_ty = value.ty(); let expect = self.literal_operand(test.span, value); - if let ty::Adt(def, _) = ty.kind() - && tcx.is_lang_item(def.did(), LangItem::String) - { - if !tcx.features().string_deref_patterns() { - span_bug!( + + let mut place = place; + let mut block = block; + match ty.kind() { + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { + if !tcx.features().string_deref_patterns() { + span_bug!( + test.span, + "matching on `String` went through without enabling string_deref_patterns" + ); + } + let re_erased = tcx.lifetimes.re_erased; + let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); + let ref_str = self.temp(ref_str_ty, test.span); + let eq_block = self.cfg.start_new_block(); + // `let ref_str: &str = ::deref(&place);` + self.call_deref( + block, + eq_block, + place, + Mutability::Not, + ty, + ref_str, test.span, - "matching on `String` went through without enabling string_deref_patterns" ); + // Since we generated a `ref_str = ::deref(&place) -> eq_block` terminator, + // we need to add all further statements to `eq_block`. + // Similarly, the normal test code should be generated for the `&str`, instead of the `String`. + block = eq_block; + place = ref_str; + ty = ref_str_ty; } - let re_erased = tcx.lifetimes.re_erased; - let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); - let ref_str = self.temp(ref_str_ty, test.span); - let eq_block = self.cfg.start_new_block(); - // `let ref_str: &str = ::deref(&place);` - self.call_deref( - block, - eq_block, - place, - Mutability::Not, - ty, - ref_str, - test.span, - ); - self.non_scalar_compare( - eq_block, - success_block, - fail_block, - source_info, - expect, - expect_ty, - Operand::Copy(ref_str), - ref_str_ty, - ); - } else if !ty.is_scalar() { + _ => {} + } + + if !ty.is_scalar() { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) self.non_scalar_compare( From 1d8f59ff5107ec205a919c0becc9e216e17b6c7d Mon Sep 17 00:00:00 2001 From: nora <48135649+Noratrieb@users.noreply.github.com> Date: Tue, 28 Jan 2025 10:51:30 +0100 Subject: [PATCH 202/342] Update username in build helper example --- src/build_helper/src/git.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 01bac1498c24..3ef9c7ac35e9 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -30,8 +30,8 @@ pub fn output_result(cmd: &mut Command) -> Result { /// Finds the remote for rust-lang/rust. /// For example for these remotes it will return `upstream`. /// ```text -/// origin https://github.com/Nilstrieb/rust.git (fetch) -/// origin https://github.com/Nilstrieb/rust.git (push) +/// origin https://github.com/pietroalbani/rust.git (fetch) +/// origin https://github.com/pietroalbani/rust.git (push) /// upstream https://github.com/rust-lang/rust (fetch) /// upstream https://github.com/rust-lang/rust (push) /// ``` From 75b39d00abc3a8b4f974673e4948152246dc3824 Mon Sep 17 00:00:00 2001 From: lapla-cogito Date: Mon, 27 Jan 2025 11:41:10 +0900 Subject: [PATCH 203/342] exclude specific directories from `Lintcheck` --- .github/workflows/lintcheck.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml index 64966f1d1898..d487c7d94985 100644 --- a/.github/workflows/lintcheck.yml +++ b/.github/workflows/lintcheck.yml @@ -1,6 +1,12 @@ name: Lintcheck -on: pull_request +on: + pull_request: + paths-ignore: + - 'book/**' + - 'util/**' + - 'tests/**' + - '*.md' env: RUST_BACKTRACE: 1 From eee9df43e69e9841aaaa9bd4eae9f600a81f20b1 Mon Sep 17 00:00:00 2001 From: SpecificProtagonist Date: Sat, 25 Jan 2025 00:54:51 +0100 Subject: [PATCH 204/342] miri: optimize zeroed alloc Co-authored-by: Ralf Jung --- .../src/const_eval/machine.rs | 8 +++--- .../rustc_const_eval/src/interpret/memory.rs | 21 ++++++++++----- .../rustc_const_eval/src/interpret/place.rs | 8 +++--- .../rustc_const_eval/src/interpret/util.rs | 4 +-- .../src/mir/interpret/allocation.rs | 26 ++++++++++++++----- .../rustc_middle/src/mir/interpret/mod.rs | 4 +-- compiler/rustc_middle/src/ty/vtable.rs | 6 +++-- compiler/rustc_smir/src/rustc_smir/alloc.rs | 12 ++++++--- src/tools/miri/src/shims/alloc.rs | 19 +++++--------- src/tools/miri/src/shims/foreign_items.rs | 16 +++++------- src/tools/miri/src/shims/unix/fs.rs | 1 + src/tools/miri/src/shims/unix/linux/mem.rs | 10 +------ src/tools/miri/src/shims/unix/mem.rs | 16 +++++------- .../miri/src/shims/windows/foreign_items.rs | 16 ++++++------ 14 files changed, 88 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cfdfbdb7880b..6a339d695426 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,10 @@ use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok, - throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, + compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, + throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -420,6 +421,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { Size::from_bytes(size), align, interpret::MemoryKind::Machine(MemoryKind::Heap), + AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2772c94d52b0..1b8a222e9eaa 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -20,10 +20,10 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use tracing::{debug, instrument, trace}; use super::{ - AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, - CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer, - PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, + CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, + Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, + err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -230,11 +230,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: Size, align: Align, kind: MemoryKind, + init: AllocInit, ) -> InterpResult<'tcx, Pointer> { let alloc = if M::PANIC_ON_ALLOC_FAIL { - Allocation::uninit(size, align) + Allocation::new(size, align, init) } else { - Allocation::try_uninit(size, align)? + Allocation::try_new(size, align, init)? }; self.insert_allocation(alloc, kind) } @@ -270,6 +271,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) } + /// If this grows the allocation, `init_growth` determines + /// whether the additional space will be initialized. pub fn reallocate_ptr( &mut self, ptr: Pointer>, @@ -277,6 +280,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { new_size: Size, new_align: Align, kind: MemoryKind, + init_growth: AllocInit, ) -> InterpResult<'tcx, Pointer> { let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { @@ -289,7 +293,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". // This happens so rarely, the perf advantage is outweighed by the maintenance cost. - let new_ptr = self.allocate_ptr(new_size, new_align, kind)?; + // If requested, we zero-init the entire allocation, to ensure that a growing + // allocation has its new bytes properly set. For the part that is copied, + // `mem_copy` below will de-initialize things as necessary. + let new_ptr = self.allocate_ptr(new_size, new_align, kind, init_growth)?; let old_size = match old_size_and_align { Some((size, _align)) => size, None => self.get_alloc_raw(alloc_id)?.size(), diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index c97922ac132b..f5d3de7b1b27 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -12,9 +12,9 @@ use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, - Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, - Scalar, alloc_range, interp_ok, mir_assign_valid_types, + AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, + InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, + Projectable, Provenance, Scalar, alloc_range, interp_ok, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -983,7 +983,7 @@ where let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else { span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; - let ptr = self.allocate_ptr(size, align, kind)?; + let ptr = self.allocate_ptr(size, align, kind, AllocInit::Uninit)?; interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index ecb7c3fc93cd..eb98e3b53805 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; -use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; +use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -76,7 +76,7 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?; + let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit)?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1b07846e0cf6..2e4d258ffff2 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -270,6 +270,12 @@ impl AllocRange { } } +/// Whether a new allocation should be initialized with zero-bytes. +pub enum AllocInit { + Uninit, + Zero, +} + // The constructors are all without extra; the extra gets added by a machine hook later. impl Allocation { /// Creates an allocation initialized by the given bytes @@ -294,7 +300,12 @@ impl Allocation { Allocation::from_bytes(slice, Align::ONE, Mutability::Not) } - fn uninit_inner(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result { + fn new_inner( + size: Size, + align: Align, + init: AllocInit, + fail: impl FnOnce() -> R, + ) -> Result { // We raise an error if we cannot create the allocation on the host. // This results in an error that can happen non-deterministically, since the memory // available to the compiler can change between runs. Normally queries are always @@ -306,7 +317,10 @@ impl Allocation { Ok(Allocation { bytes, provenance: ProvenanceMap::new(), - init_mask: InitMask::new(size, false), + init_mask: InitMask::new(size, match init { + AllocInit::Uninit => false, + AllocInit::Zero => true, + }), align, mutability: Mutability::Mut, extra: (), @@ -315,8 +329,8 @@ impl Allocation { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. - pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { - Self::uninit_inner(size, align, || { + pub fn try_new<'tcx>(size: Size, align: Align, init: AllocInit) -> InterpResult<'tcx, Self> { + Self::new_inner(size, align, init, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) @@ -328,8 +342,8 @@ impl Allocation { /// /// Example use case: To obtain an Allocation filled with specific data, /// first call this function and then call write_scalar to fill in the right data. - pub fn uninit(size: Size, align: Align) -> Self { - match Self::uninit_inner(size, align, || { + pub fn new(size: Size, align: Align, init: AllocInit) -> Self { + match Self::new_inner(size, align, init, || { panic!( "interpreter ran out of memory: cannot create allocation of {} bytes", size.bytes() diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b88137544bca..f4f9f221a751 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -30,8 +30,8 @@ pub use { }; pub use self::allocation::{ - AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk, - InitChunkIter, alloc_range, + AllocBytes, AllocError, AllocInit, AllocRange, AllocResult, Allocation, ConstAllocation, + InitChunk, InitChunkIter, alloc_range, }; pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 23e2e8ad3d33..455bd16ff8c2 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -4,7 +4,9 @@ use rustc_ast::Mutability; use rustc_macros::HashStable; use rustc_type_ir::elaborate; -use crate::mir::interpret::{AllocId, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range}; +use crate::mir::interpret::{ + AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range, +}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; #[derive(Clone, Copy, PartialEq, HashStable)] @@ -108,7 +110,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let ptr_align = tcx.data_layout.pointer_align.abi; let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); - let mut vtable = Allocation::uninit(vtable_size, ptr_align); + let mut vtable = Allocation::new(vtable_size, ptr_align, AllocInit::Uninit); // No need to do any alignment checks on the memory accesses below, because we know the // allocation is correctly aligned as we created it above. Also we're only offsetting by diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 4e8db6096d4f..52c5b425c14f 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -1,6 +1,6 @@ use rustc_abi::{Align, Size}; use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{AllocRange, Pointer, alloc_range}; +use rustc_middle::mir::interpret::{AllocInit, AllocRange, Pointer, alloc_range}; use stable_mir::Error; use stable_mir::mir::Mutability; use stable_mir::ty::{Allocation, ProvenanceMap}; @@ -44,7 +44,8 @@ pub(crate) fn try_new_allocation<'tcx>( .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))? .align; - let mut allocation = rustc_middle::mir::interpret::Allocation::uninit(size, align.abi); + let mut allocation = + rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) .map_err(|e| e.stable(tables))?; @@ -68,8 +69,11 @@ pub(crate) fn try_new_allocation<'tcx>( .tcx .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))?; - let mut allocation = - rustc_middle::mir::interpret::Allocation::uninit(layout.size, layout.align.abi); + let mut allocation = rustc_middle::mir::interpret::Allocation::new( + layout.size, + layout.align.abi, + AllocInit::Uninit, + ); allocation .write_scalar( &tables.tcx, diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 25c0b52d0618..0fda13e06160 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -1,5 +1,3 @@ -use std::iter; - use rustc_abi::{Align, Size}; use rustc_ast::expand::allocator::AllocatorKind; @@ -80,18 +78,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - fn malloc(&mut self, size: u64, zero_init: bool) -> InterpResult<'tcx, Pointer> { + fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let align = this.malloc_align(size); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } + let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; interp_ok(ptr.into()) } @@ -115,6 +105,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), + AllocInit::Uninit )?; this.write_pointer(ptr, &memptr)?; interp_ok(Scalar::from_i32(0)) @@ -134,7 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_align = this.malloc_align(new_size); if this.ptr_is_null(old_ptr)? { // Here we must behave like `malloc`. - self.malloc(new_size, /*zero_init*/ false) + self.malloc(new_size, AllocInit::Uninit) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. @@ -147,6 +138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), new_align, MiriMemoryKind::C.into(), + AllocInit::Uninit )?; interp_ok(new_ptr.into()) } @@ -187,6 +179,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), + AllocInit::Uninit )?; interp_ok(ptr.into()) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 6c8ccc839859..1ce0c209de9e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -1,6 +1,5 @@ use std::collections::hash_map::Entry; use std::io::Write; -use std::iter; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, Size}; @@ -9,6 +8,7 @@ use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::AllocInit; use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; use rustc_span::Symbol; @@ -442,7 +442,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [size] = this.check_shim(abi, Conv::C, link_name, args)?; let size = this.read_target_usize(size)?; if size <= this.max_size_of_val().bytes() { - let res = this.malloc(size, /*zero_init:*/ false)?; + let res = this.malloc(size, AllocInit::Uninit)?; this.write_pointer(res, dest)?; } else { // If this does not fit in an isize, return null and, on Unix, set errno. @@ -457,7 +457,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let items = this.read_target_usize(items)?; let elem_size = this.read_target_usize(elem_size)?; if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) { - let res = this.malloc(size.bytes(), /*zero_init:*/ true)?; + let res = this.malloc(size.bytes(), AllocInit::Zero)?; this.write_pointer(res, dest)?; } else { // On size overflow, return null and, on Unix, set errno. @@ -509,6 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), memory_kind.into(), + AllocInit::Uninit )?; ecx.write_pointer(ptr, dest) @@ -537,14 +538,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::Rust.into(), + AllocInit::Zero )?; - - // We just allocated this, the access is definitely in-bounds. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); this.write_pointer(ptr, dest) }); } @@ -604,6 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Rust.into(), + AllocInit::Uninit )?; this.write_pointer(new_ptr, dest) }); diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 25594b780318..cafce62cfedb 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1109,6 +1109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), dirent_layout.align.abi, MiriMemoryKind::Runtime.into(), + AllocInit::Uninit )?; let entry: Pointer = entry.into(); diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 8e796d5dce55..6418d749d3d9 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -49,16 +49,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Mmap.into(), + AllocInit::Zero )?; - if let Some(increase) = new_size.checked_sub(old_size) { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.wrapping_offset(Size::from_bytes(old_size), this).into(), - std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), - ) - .unwrap(); - } interp_ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 5531b944e17d..2d5d3a6471ab 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -111,15 +111,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(this.eval_libc("MAP_FAILED")); } - let ptr = - this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?; - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.into(), - std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()), - ) - .unwrap(); + let ptr = this.allocate_ptr( + Size::from_bytes(map_length), + align, + MiriMemoryKind::Mmap.into(), + // mmap guarantees new mappings are zero-init. + AllocInit::Zero + )?; interp_ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 0bf56c3d005f..4462d025bead 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -253,8 +253,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_target_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; let size = this.read_target_usize(size)?; - let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY - let zero_init = (flags & heap_zero_memory) == heap_zero_memory; + const HEAP_ZERO_MEMORY: u32 = 0x00000008; + let init = if (flags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY { + AllocInit::Zero + } else { + AllocInit::Uninit + }; // Alignment is twice the pointer size. // Source: let align = this.tcx.pointer_size().bytes().strict_mul(2); @@ -262,13 +266,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), + init )?; - if zero_init { - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - )?; - } this.write_pointer(ptr, dest)?; } "HeapFree" => { @@ -300,6 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), + AllocInit::Uninit )?; this.write_pointer(new_ptr, dest)?; } From 4203627cedc5a6b5a7b7605888b5c33876e2b0e2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sat, 23 Nov 2024 23:47:45 +0800 Subject: [PATCH 205/342] Suggest considering casting fn item as fn pointer in more cases --- compiler/rustc_trait_selection/messages.ftl | 2 + .../src/error_reporting/infer/mod.rs | 2 +- .../src/error_reporting/infer/suggest.rs | 39 ++++++++++-- .../traits/fulfillment_errors.rs | 1 + compiler/rustc_trait_selection/src/errors.rs | 6 ++ .../casting-fn-item-to-fn-pointer.rs | 9 +++ .../casting-fn-item-to-fn-pointer.stderr | 59 +++++++++++++++++++ tests/ui/typeck/issue-107775.stderr | 2 + 8 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs create mode 100644 tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 1ab89ecde7a4..7bfc02c7909c 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -165,6 +165,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` +trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}` + trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same trait_selection_fps_cast = consider casting to a fn pointer trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 929fa559d750..ff526b0cc5d6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1661,7 +1661,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_tuple_pattern(cause, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); - self.suggest_function_pointers(cause, span, &exp_found, diag); + self.suggest_function_pointers(cause, span, &exp_found, terr, diag); self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index fc2d0ba36f04..d2d481260786 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -12,6 +12,7 @@ use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, StatementAsExpression, }; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{Span, sym}; @@ -20,7 +21,7 @@ use tracing::debug; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::hir::Path; use crate::errors::{ - ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; @@ -369,14 +370,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_function_pointers( + pub fn suggest_function_pointers_impl( &self, - cause: &ObligationCause<'tcx>, - span: Span, + span: Option, exp_found: &ty::error::ExpectedFound>, diag: &mut Diag<'_>, ) { - debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); let ty::error::ExpectedFound { expected, found } = exp_found; let expected_inner = expected.peel_refs(); let found_inner = found.peel_refs(); @@ -399,6 +398,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } + let Some(span) = span else { + let casting = format!("{fn_name} as {sig}"); + diag.subdiagnostic(FnItemsAreDistinct); + diag.subdiagnostic(FnConsiderCasting { casting }); + return; + }; + let sugg = match (expected.is_ref(), found.is_ref()) { (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, @@ -433,6 +439,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let fn_name = self.tcx.def_path_str_with_args(*did2, args2); + + let Some(span) = span else { + diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig }); + return; + }; + let sug = if found.is_ref() { FunctionPointerSuggestion::CastBothRef { span, @@ -476,6 +488,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; } + pub(super) fn suggest_function_pointers( + &self, + cause: &ObligationCause<'tcx>, + span: Span, + exp_found: &ty::error::ExpectedFound>, + terr: TypeError<'tcx>, + diag: &mut Diag<'_>, + ) { + debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); + + if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() { + self.suggest_function_pointers_impl(Some(span), exp_found, diag); + } else if let TypeError::Sorts(exp_found) = terr { + self.suggest_function_pointers_impl(None, &exp_found, diag); + } + } + pub fn should_suggest_as_ref_kind( &self, expected: Ty<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1109b11d2a71..294c20c629c8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1969,6 +1969,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted(exp_found.found.to_string()), StringPart::normal("`"), ]); + self.suggest_function_pointers_impl(None, &exp_found, err); } true diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index afac6fc6004c..bd9751c117ea 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1498,6 +1498,12 @@ pub struct FnConsiderCasting { pub casting: String, } +#[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting_both)] +pub struct FnConsiderCastingBoth<'a> { + pub sig: Binder<'a, FnSig<'a>>, +} + #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs new file mode 100644 index 000000000000..fa1663d49eb2 --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs @@ -0,0 +1,9 @@ +//@ edition: 2021 + +fn foo() {} + +fn main() { + let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR + let _: Vec = [foo].into_iter().collect(); //~ ERROR + let _: Vec = Vec::from([foo]); //~ ERROR +} diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr new file mode 100644 index 000000000000..d069d39514dc --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr @@ -0,0 +1,59 @@ +error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})` + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:59 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>` + but trait `FromIterator<(&_, fn())>` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:47 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here + | | + | this expression has type `[(&str, fn() {foo}); 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `fn() {foo}` + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:42 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator` is not implemented for `Vec` + but trait `FromIterator` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:30 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here + | | + | this expression has type `[fn() {foo}; 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0308]: mismatched types + --> $DIR/casting-fn-item-to-fn-pointer.rs:8:24 + | +LL | let _: Vec = Vec::from([foo]); + | --------- ^^^^^^^^^^^^^^^^ expected `Vec`, found `Vec` + | | + | expected due to this + | + = note: expected struct `Vec` + found struct `Vec` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr index 180b0183a3f2..dad7e1581e79 100644 --- a/tests/ui/typeck/issue-107775.stderr +++ b/tests/ui/typeck/issue-107775.stderr @@ -10,6 +10,8 @@ LL | Self { map } | = note: expected struct `HashMap Pin + Send + 'static)>>>` found struct `HashMap<{integer}, fn(_) -> Pin + Send>> {::do_something::<'_>}>` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `::do_something::<'_> as fn(u8) -> Pin + Send + 'static)>>` error: aborting due to 1 previous error From aa1f941116e62aca07ea916bfef605b19793e058 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 22 Jan 2025 09:30:28 +0100 Subject: [PATCH 206/342] Fix platform-specific doc string for AtomicUsize::from_mut to be platform-independent --- library/core/src/sync/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index fda26a672990..f7aa83818551 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2297,7 +2297,7 @@ macro_rules! atomic_int { $int_type, no = [ "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." + stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." ], }] /// From 9c4fd25f278d1a04e4d3cd5138bf8ffd9ce52e6a Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 28 Jan 2025 19:10:28 +0530 Subject: [PATCH 207/342] uefi: process: Fix args - While working on process env support, I found that args were currently broken. Not sure how I missed it in the PR, but well here is the fix. - Additionally, no point in adding space at the end of args. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/process.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1a0754134dfb..3077a72eac66 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -460,7 +460,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -706,9 +706,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,8 +720,6 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() From 356b2aa42234f8089f878a2857a303de8717ea7f Mon Sep 17 00:00:00 2001 From: Boxy Date: Wed, 22 Jan 2025 17:35:10 +0000 Subject: [PATCH 208/342] "normalize" signature before checking mentions self --- compiler/rustc_hir_typeck/src/closure.rs | 44 ++++++++++++++-- .../src/fn_ctxt/inspect_obligations.rs | 17 +++++- tests/ui/borrowck/issue-103095.rs | 8 +-- .../supertrait-hint-references-assoc-ty.rs | 3 ++ ...rg-type-mismatch-issue-45727.current.fixed | 1 + ...-arg-type-mismatch-issue-45727.next.stderr | 20 +++++-- .../closure-arg-type-mismatch-issue-45727.rs | 1 + ...re-inference-hr-ambig-alias-naming-self.rs | 52 +++++++++++++++++++ 8 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b8652d82d91b..4dcc83d0aefb 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -296,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given the expected type, figures out what it can about this closure we /// are about to type check: - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn deduce_closure_signature( &self, expected_ty: Ty<'tcx>, @@ -378,6 +378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_predicate.rebind(proj_predicate), ), ); + // Make sure that we didn't infer a signature that mentions itself. // This can happen when we elaborate certain supertrait bounds that // mention projections containing the `Self` type. See #105401. @@ -395,8 +396,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { - expected_sig = inferred_sig; + + // Don't infer a closure signature from a goal that names the closure type as this will + // (almost always) lead to occurs check errors later in type checking. + if self.next_trait_solver() + && let Some(inferred_sig) = inferred_sig + { + // In the new solver it is difficult to explicitly normalize the inferred signature as we + // would have to manually handle universes and rewriting bound vars and placeholders back + // and forth. + // + // Instead we take advantage of the fact that we relating an inference variable with an alias + // will only instantiate the variable if the alias is rigid(*not quite). Concretely we: + // - Create some new variable `?sig` + // - Equate `?sig` with the unnormalized signature, e.g. `fn( as Trait>::Assoc)` + // - Depending on whether ` as Trait>::Assoc` is rigid, ambiguous or normalizeable, + // we will either wind up with `?sig= as Trait>::Assoc/?y/ConcreteTy` respectively. + // + // *: In cases where there are ambiguous aliases in the signature that make use of bound vars + // they will wind up present in `?sig` even though they are non-rigid. + // + // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` + // even though the normalized form may not name `expected_ty`. However, this matches the existing + // behaviour of the old solver and would be technically a breaking change to fix. + let generalized_fnptr_sig = self.next_ty_var(span); + let inferred_fnptr_sig = Ty::new_fn_ptr(self.tcx, inferred_sig.sig); + self.demand_eqtype(span, inferred_fnptr_sig, generalized_fnptr_sig); + + let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig); + + if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = Some(ExpectedSig { + cause_span: inferred_sig.cause_span, + sig: resolved_sig.fn_sig(self.tcx), + }); + } + } else { + if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = inferred_sig; + } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index eb5fe3a86e4c..dc10b53fd839 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, }; +use rustc_type_ir::solve::GoalSource; use tracing::{debug, instrument, trace}; use crate::FnCtxt; @@ -119,7 +120,21 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) + // We do not push the instantiated forms of goals as it would cause any + // aliases referencing bound vars to go from having escaping bound vars to + // being able to be normalized to an inference variable. + // + // This is mostly just a hack as arbitrary nested goals could still contain + // such aliases while having a different `GoalSource`. Closure signature inference + // however can't really handle *every* higher ranked `Fn` goal also being present + // in the form of `?c: Fn<(>::Assoc)`. + // + // This also just better matches the behaviour of the old solver where we do not + // encounter instantiated forms of goals, only nested goals that referred to bound + // vars from instantiated goals. + && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked) + { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), diff --git a/tests/ui/borrowck/issue-103095.rs b/tests/ui/borrowck/issue-103095.rs index 3c29bc761553..53587a16abf8 100644 --- a/tests/ui/borrowck/issue-103095.rs +++ b/tests/ui/borrowck/issue-103095.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait FnOnceForGenericRef: FnOnce(&T) -> Self::FnOutput { type FnOutput; @@ -16,10 +19,7 @@ struct Data> { impl> Data { fn new(value: T, f: D) -> Self { let output = f(&value); - Self { - value: Some(value), - output: Some(output), - } + Self { value: Some(value), output: Some(output) } } } diff --git a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs index fa74ffc5bec5..b6a1685cb720 100644 --- a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs +++ b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver pub trait Fn0: Fn(i32) -> Self::Out { type Out; diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index 7383ab177dc3..25943d11fc47 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -9,4 +9,5 @@ fn main() { let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 6104a0893373..696214c0a3cd 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -13,12 +13,26 @@ note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0271]: expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:33 + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24 | LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); - | ^^^^^^ expected `&&i32`, found integer + | ^^^^ expected `&&i32`, found integer -error: aborting due to 2 previous errors +error[E0277]: expected a `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:29 + | +LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> FnMut(&'a as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + = note: expected a closure with arguments `(&&&i32,)` + found a closure with arguments `(& as Iterator>::Item,)` +note: required by a bound in `find` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0271, E0277. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 668a1a7a29c6..9e44489cbf17 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -9,4 +9,5 @@ fn main() { let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs b/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs new file mode 100644 index 000000000000..25649d929032 --- /dev/null +++ b/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs @@ -0,0 +1,52 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// When type checking a closure expr we look at the list of unsolved goals +// to determine if there are any bounds on the closure type to infer a signature from. +// +// We attempt to discard goals that name the closure type so as to avoid inferring the +// closure type to something like `?x = closure(sig=fn(?x))`. This test checks that when +// such a goal names the closure type inside of an ambiguous alias and there exists another +// potential goal to infer the closure signature from, we do that. + +trait Trait<'a> { + type Assoc; +} + +impl<'a, F> Trait<'a> for F { + type Assoc = u32; +} + +fn closure_typer1(_: F) +where + F: Fn(u32) + for<'a> Fn(>::Assoc), +{ +} + +fn closure_typer2(_: F) +where + F: for<'a> Fn(>::Assoc) + Fn(u32), +{ +} + +fn main() { + // Here we have some closure with a yet to be inferred type of `?c`. There are two goals + // involving `?c` that can be used to determine the closure signature: + // - `?c: for<'a> Fn<(>::Assoc,), Output = ()>` + // - `?c: Fn<(u32,), Output = ()>` + // + // If we were to infer the argument of the closure (`x` below) to `>::Assoc` + // then we would not be able to call `x.into()` as `x` is some unknown type. Instead we must + // use the `?c: Fn(u32)` goal to infer a signature in order for this code to compile. + // + // As the algorithm for picking a goal to infer the signature from is dependent on the ordering + // of pending goals in the type checker, we test both orderings of bounds to ensure we aren't + // testing that we just *happen* to pick `?c: Fn(u32)`. + closure_typer1(move |x| { + let _: u32 = x.into(); + }); + closure_typer2(move |x| { + let _: u32 = x.into(); + }); +} From cfb8be52b35d156c7bb4e48feaab93afa2055abc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 28 Jan 2025 23:49:02 +0900 Subject: [PATCH 209/342] Remove duplicated code in RISC-V asm bad-reg test --- tests/ui/asm/riscv/bad-reg.riscv32e.stderr | 66 +++++++++---------- tests/ui/asm/riscv/bad-reg.riscv32gc.stderr | 26 +++----- tests/ui/asm/riscv/bad-reg.riscv32i.stderr | 34 ++++------ .../ui/asm/riscv/bad-reg.riscv32imafc.stderr | 30 ++++----- tests/ui/asm/riscv/bad-reg.riscv64gc.stderr | 26 +++----- tests/ui/asm/riscv/bad-reg.riscv64imac.stderr | 34 ++++------ tests/ui/asm/riscv/bad-reg.rs | 2 - 7 files changed, 90 insertions(+), 128 deletions(-) diff --git a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr index 409178df9c5a..27c8e958e536 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr @@ -22,170 +22,164 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: cannot use register `x16`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("x16") _); | ^^^^^^^^^^^^ error: cannot use register `x17`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("x17") _); | ^^^^^^^^^^^^ error: cannot use register `x18`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:50:18 | LL | asm!("", out("x18") _); | ^^^^^^^^^^^^ error: cannot use register `x19`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:52:18 | LL | asm!("", out("x19") _); | ^^^^^^^^^^^^ error: cannot use register `x20`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:54:18 | LL | asm!("", out("x20") _); | ^^^^^^^^^^^^ error: cannot use register `x21`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:56:18 | LL | asm!("", out("x21") _); | ^^^^^^^^^^^^ error: cannot use register `x22`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:58:18 | LL | asm!("", out("x22") _); | ^^^^^^^^^^^^ error: cannot use register `x23`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("x23") _); | ^^^^^^^^^^^^ error: cannot use register `x24`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:62:18 | LL | asm!("", out("x24") _); | ^^^^^^^^^^^^ error: cannot use register `x25`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:64:18 | LL | asm!("", out("x25") _); | ^^^^^^^^^^^^ error: cannot use register `x26`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("x26") _); | ^^^^^^^^^^^^ error: cannot use register `x27`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:68:18 | LL | asm!("", out("x27") _); | ^^^^^^^^^^^^ error: cannot use register `x28`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:72:18 + --> $DIR/bad-reg.rs:70:18 | LL | asm!("", out("x28") _); | ^^^^^^^^^^^^ error: cannot use register `x29`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:74:18 + --> $DIR/bad-reg.rs:72:18 | LL | asm!("", out("x29") _); | ^^^^^^^^^^^^ error: cannot use register `x30`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:76:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", out("x30") _); | ^^^^^^^^^^^^ error: cannot use register `x31`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:78:18 + --> $DIR/bad-reg.rs:76:18 | LL | asm!("", out("x31") _); | ^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -193,7 +187,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -201,12 +195,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 34 previous errors +error: aborting due to 33 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr index 4770e70cc2b7..4ff03d819e60 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr index ae7db1554b19..fbe63eb0563c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr index 8bc5c9a87fce..57664cfe893b 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:86:35 + --> $DIR/bad-reg.rs:84:35 | LL | asm!("/* {} */", in(freg) d); | ^ @@ -73,7 +67,7 @@ LL | asm!("/* {} */", in(freg) d); = note: this is required to use type `f64` with register class `freg` error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:89:36 + --> $DIR/bad-reg.rs:87:36 | LL | asm!("/* {} */", out(freg) d); | ^ @@ -81,7 +75,7 @@ LL | asm!("/* {} */", out(freg) d); = note: this is required to use type `f64` with register class `freg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -89,7 +83,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -97,12 +91,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr index 4770e70cc2b7..4ff03d819e60 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr index ae7db1554b19..fbe63eb0563c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.rs b/tests/ui/asm/riscv/bad-reg.rs index 7f0fc00d5489..7d032d277aa9 100644 --- a/tests/ui/asm/riscv/bad-reg.rs +++ b/tests/ui/asm/riscv/bad-reg.rs @@ -38,8 +38,6 @@ fn f() { //~^ ERROR invalid register `sp`: the stack pointer cannot be used as an operand for inline asm asm!("", out("gp") _); //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm - asm!("", out("gp") _); - //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm asm!("", out("tp") _); //~^ ERROR invalid register `tp`: the thread pointer cannot be used as an operand for inline asm asm!("", out("zero") _); From 6509596dd7c663ea2b72c5c92ae025a8d585c476 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:02:38 +0100 Subject: [PATCH 210/342] ci: remove unused windows runner --- src/ci/github-actions/jobs.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 7730d29d28f6..d5c949a0eb7a 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -43,10 +43,6 @@ runners: os: windows-2022-8core-32gb <<: *base-job - - &job-windows-16c - os: windows-2022-16core-64gb - <<: *base-job - - &job-aarch64-linux # Free some disk space to avoid running out of space during the build. free_disk: true From 5df51930f926735f2ec48433e95502c3b214cdf8 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Tue, 28 Jan 2025 19:02:15 +0300 Subject: [PATCH 211/342] Fix tests/codegen/float/f128 --- src/tools/compiletest/src/directive-list.rs | 1 + tests/codegen/float/f128.rs | 31 ++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index acdb3cbdd459..71496444660f 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -177,6 +177,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-bpf", "only-cdb", "only-dist", + "only-emscripten", "only-gnu", "only-i686-pc-windows-gnu", "only-i686-pc-windows-msvc", diff --git a/tests/codegen/float/f128.rs b/tests/codegen/float/f128.rs index 514d35433e12..562a8e6c9e9b 100644 --- a/tests/codegen/float/f128.rs +++ b/tests/codegen/float/f128.rs @@ -1,11 +1,15 @@ // 32-bit x86 returns float types differently to avoid the x87 stack. // 32-bit systems will return 128bit values using a return area pointer. -//@ revisions: x86 bit32 bit64 +// Emscripten aligns f128 to 8 bytes, not 16. +//@ revisions: x86 bit32 bit64 emscripten //@[x86] only-x86 //@[bit32] ignore-x86 +//@[bit32] ignore-emscripten //@[bit32] only-32bit //@[bit64] ignore-x86 +//@[bit64] ignore-emscripten //@[bit64] only-64bit +//@[emscripten] only-emscripten // Verify that our intrinsics generate the correct LLVM calls for f128 @@ -59,6 +63,7 @@ pub fn f128_le(a: f128, b: f128) -> bool { // x86-LABEL: void @f128_neg({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_neg({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_neg( +// emscripten-LABEL: void @f128_neg({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_neg(a: f128) -> f128 { // CHECK: fneg fp128 @@ -68,6 +73,7 @@ pub fn f128_neg(a: f128) -> f128 { // x86-LABEL: void @f128_add({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_add({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_add( +// emscripten-LABEL: void @f128_add({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_add(a: f128, b: f128) -> f128 { // CHECK: fadd fp128 %{{.+}}, %{{.+}} @@ -77,6 +83,7 @@ pub fn f128_add(a: f128, b: f128) -> f128 { // x86-LABEL: void @f128_sub({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_sub({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_sub( +// emscripten-LABEL: void @f128_sub({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_sub(a: f128, b: f128) -> f128 { // CHECK: fsub fp128 %{{.+}}, %{{.+}} @@ -86,6 +93,7 @@ pub fn f128_sub(a: f128, b: f128) -> f128 { // x86-LABEL: void @f128_mul({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_mul({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_mul( +// emscripten-LABEL: void @f128_mul({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_mul(a: f128, b: f128) -> f128 { // CHECK: fmul fp128 %{{.+}}, %{{.+}} @@ -95,6 +103,7 @@ pub fn f128_mul(a: f128, b: f128) -> f128 { // x86-LABEL: void @f128_div({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_div({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_div( +// emscripten-LABEL: void @f128_div({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_div(a: f128, b: f128) -> f128 { // CHECK: fdiv fp128 %{{.+}}, %{{.+}} @@ -104,6 +113,7 @@ pub fn f128_div(a: f128, b: f128) -> f128 { // x86-LABEL: void @f128_rem({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_rem({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_rem( +// emscripten-LABEL: void @f128_rem({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_rem(a: f128, b: f128) -> f128 { // CHECK: frem fp128 %{{.+}}, %{{.+}} @@ -164,6 +174,7 @@ pub fn f128_as_f16(a: f128) -> f16 { // x86-LABEL: i32 @f128_as_f32( // bit32-LABEL: float @f128_as_f32( // bit64-LABEL: float @f128_as_f32( +// emscripten-LABEL: float @f128_as_f32( #[no_mangle] pub fn f128_as_f32(a: f128) -> f32 { // CHECK: fptrunc fp128 %{{.+}} to float @@ -173,6 +184,7 @@ pub fn f128_as_f32(a: f128) -> f32 { // x86-LABEL: void @f128_as_f64( // bit32-LABEL: double @f128_as_f64( // bit64-LABEL: double @f128_as_f64( +// emscripten-LABEL: double @f128_as_f64( #[no_mangle] pub fn f128_as_f64(a: f128) -> f64 { // CHECK: fptrunc fp128 %{{.+}} to double @@ -182,17 +194,20 @@ pub fn f128_as_f64(a: f128) -> f64 { // x86-LABEL: void @f128_as_self({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_as_self({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f128_as_self( +// emscripten-LABEL: void @f128_as_self({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_as_self(a: f128) -> f128 { // x86: store fp128 %a, ptr %_0, align 16 // bit32: store fp128 %a, ptr %_0, align 16 // bit64: ret fp128 %{{.+}} + // emscripten: store fp128 %a, ptr %_0, align 8 a as f128 } // x86-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f16_as_f128( +// emscripten-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn f16_as_f128(a: f16) -> f128 { // CHECK: fpext half %{{.+}} to fp128 @@ -202,6 +217,7 @@ pub fn f16_as_f128(a: f16) -> f128 { // x86-LABEL: void @f32_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @f32_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f32_as_f128( +// emscripten-LABEL: void @f32_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn f32_as_f128(a: f32) -> f128 { // CHECK: fpext float %{{.+}} to fp128 @@ -211,6 +227,7 @@ pub fn f32_as_f128(a: f32) -> f128 { // x86-LABEL: void @f64_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @f64_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @f64_as_f128( +// emscripten-LABEL: void @f64_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn f64_as_f128(a: f64) -> f128 { // CHECK: fpext double %{{.+}} to fp128 @@ -249,6 +266,7 @@ pub fn f128_as_u64(a: f128) -> u64 { // x86-LABEL: void @f128_as_u128({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_as_u128({{.*}}sret([16 x i8]) // bit64-LABEL: i128 @f128_as_u128( +// emscripten-LABEL: void @f128_as_u128({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_as_u128(a: f128) -> u128 { // CHECK: call i128 @llvm.fptoui.sat.i128.f128(fp128 %{{.+}}) @@ -285,6 +303,7 @@ pub fn f128_as_i64(a: f128) -> i64 { // x86-LABEL: void @f128_as_i128({{.*}}sret([16 x i8]) // bit32-LABEL: void @f128_as_i128({{.*}}sret([16 x i8]) // bit64-LABEL: i128 @f128_as_i128( +// emscripten-LABEL: void @f128_as_i128({{.*}}sret([16 x i8]) #[no_mangle] pub fn f128_as_i128(a: f128) -> i128 { // CHECK: call i128 @llvm.fptosi.sat.i128.f128(fp128 %{{.+}}) @@ -296,6 +315,7 @@ pub fn f128_as_i128(a: f128) -> i128 { // x86-LABEL: void @u8_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @u8_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @u8_as_f128( +// emscripten-LABEL: void @u8_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn u8_as_f128(a: u8) -> f128 { // CHECK: uitofp i8 %{{.+}} to fp128 @@ -305,6 +325,7 @@ pub fn u8_as_f128(a: u8) -> f128 { // x86-LABEL: void @u16_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @u16_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @u16_as_f128( +// emscripten-LABEL: void @u16_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn u16_as_f128(a: u16) -> f128 { // CHECK: uitofp i16 %{{.+}} to fp128 @@ -314,6 +335,7 @@ pub fn u16_as_f128(a: u16) -> f128 { // x86-LABEL: void @u32_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @u32_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @u32_as_f128( +// emscripten-LABEL: void @u32_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn u32_as_f128(a: u32) -> f128 { // CHECK: uitofp i32 %{{.+}} to fp128 @@ -323,6 +345,7 @@ pub fn u32_as_f128(a: u32) -> f128 { // x86-LABEL: void @u64_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @u64_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @u64_as_f128( +// emscripten-LABEL: void @u64_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn u64_as_f128(a: u64) -> f128 { // CHECK: uitofp i64 %{{.+}} to fp128 @@ -332,6 +355,7 @@ pub fn u64_as_f128(a: u64) -> f128 { // x86-LABEL: void @u128_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @u128_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @u128_as_f128( +// emscripten-LABEL: void @u128_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn u128_as_f128(a: u128) -> f128 { // CHECK: uitofp i128 %{{.+}} to fp128 @@ -341,6 +365,7 @@ pub fn u128_as_f128(a: u128) -> f128 { // x86-LABEL: void @i8_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @i8_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @i8_as_f128( +// emscripten-LABEL: void @i8_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn i8_as_f128(a: i8) -> f128 { // CHECK: sitofp i8 %{{.+}} to fp128 @@ -350,6 +375,7 @@ pub fn i8_as_f128(a: i8) -> f128 { // x86-LABEL: void @i16_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @i16_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @i16_as_f128( +// emscripten-LABEL: void @i16_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn i16_as_f128(a: i16) -> f128 { // CHECK: sitofp i16 %{{.+}} to fp128 @@ -359,6 +385,7 @@ pub fn i16_as_f128(a: i16) -> f128 { // x86-LABEL: void @i32_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @i32_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @i32_as_f128( +// emscripten-LABEL: void @i32_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn i32_as_f128(a: i32) -> f128 { // CHECK: sitofp i32 %{{.+}} to fp128 @@ -368,6 +395,7 @@ pub fn i32_as_f128(a: i32) -> f128 { // x86-LABEL: void @i64_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @i64_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @i64_as_f128( +// emscripten-LABEL: void @i64_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn i64_as_f128(a: i64) -> f128 { // CHECK: sitofp i64 %{{.+}} to fp128 @@ -377,6 +405,7 @@ pub fn i64_as_f128(a: i64) -> f128 { // x86-LABEL: void @i128_as_f128({{.*}}sret([16 x i8]) // bit32-LABEL: void @i128_as_f128({{.*}}sret([16 x i8]) // bit64-LABEL: fp128 @i128_as_f128( +// emscripten-LABEL: void @i128_as_f128({{.*}}sret([16 x i8]) #[no_mangle] pub fn i128_as_f128(a: i128) -> f128 { // CHECK: sitofp i128 %{{.+}} to fp128 From efaeedef59e4f213806898cdedb2220016d193c0 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Tue, 28 Jan 2025 19:03:26 +0300 Subject: [PATCH 212/342] Fix tests/codegen/wasm_exceptions --- tests/codegen/wasm_exceptions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/wasm_exceptions.rs b/tests/codegen/wasm_exceptions.rs index 719499dd8ead..07b8ae6e9d7e 100644 --- a/tests/codegen/wasm_exceptions.rs +++ b/tests/codegen/wasm_exceptions.rs @@ -1,5 +1,5 @@ //@ only-wasm32 -//@ compile-flags: -C panic=unwind +//@ compile-flags: -C panic=unwind -Z emscripten-wasm-eh #![crate_type = "lib"] #![feature(core_intrinsics)] From 644e527c17a1b53e60a346cce7b6b32d97b9d10d Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Tue, 28 Jan 2025 19:07:12 +0300 Subject: [PATCH 213/342] Fix tests/ui/privacy/sysroot-private --- tests/ui/privacy/sysroot-private.default.stderr | 8 ++++---- tests/ui/privacy/sysroot-private.rs | 1 + .../privacy/sysroot-private.rustc_private_enabled.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/ui/privacy/sysroot-private.default.stderr b/tests/ui/privacy/sysroot-private.default.stderr index 845d4558d13a..fef88d107e63 100644 --- a/tests/ui/privacy/sysroot-private.default.stderr +++ b/tests/ui/privacy/sysroot-private.default.stderr @@ -1,11 +1,11 @@ error[E0405]: cannot find trait `Equivalent` in this scope - --> $DIR/sysroot-private.rs:26:18 + --> $DIR/sysroot-private.rs:27:18 | LL | trait Trait2: Equivalent {} | ^^^^^^^^^^ not found in this scope error[E0412]: cannot find type `K` in this scope - --> $DIR/sysroot-private.rs:31:35 + --> $DIR/sysroot-private.rs:32:35 | LL | fn trait_member(val: &T, key: &K) -> bool { | - ^ @@ -22,13 +22,13 @@ LL | fn trait_member(val: &T, key: &K) -> bool { | +++ error[E0220]: associated type `ExpressionStack` not found for `Trait` - --> $DIR/sysroot-private.rs:21:31 + --> $DIR/sysroot-private.rs:22:31 | LL | type AssociatedTy = dyn Trait; | ^^^^^^^^^^^^^^^ help: `Trait` has the following associated type: `Bar` error[E0425]: cannot find function `memchr2` in this scope - --> $DIR/sysroot-private.rs:39:5 + --> $DIR/sysroot-private.rs:40:5 | LL | memchr2(b'a', b'b', buf) | ^^^^^^^ not found in this scope diff --git a/tests/ui/privacy/sysroot-private.rs b/tests/ui/privacy/sysroot-private.rs index 67ab67c7f5c5..868185745927 100644 --- a/tests/ui/privacy/sysroot-private.rs +++ b/tests/ui/privacy/sysroot-private.rs @@ -7,6 +7,7 @@ //! of `std`'s dependencies, but may not be robust against dependency upgrades/changes. //@ only-unix Windows sysroots seem to not expose this dependency +//@ ignore-emscripten neither does Emscripten //@ revisions: default rustc_private_enabled // Enabling `rustc_private` should `std`'s dependencies accessible, so they should show up diff --git a/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr b/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr index 98e6922428a9..4b54b59714aa 100644 --- a/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr +++ b/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr @@ -1,11 +1,11 @@ error[E0405]: cannot find trait `Equivalent` in this scope - --> $DIR/sysroot-private.rs:26:18 + --> $DIR/sysroot-private.rs:27:18 | LL | trait Trait2: Equivalent {} | ^^^^^^^^^^ not found in this scope error[E0412]: cannot find type `K` in this scope - --> $DIR/sysroot-private.rs:31:35 + --> $DIR/sysroot-private.rs:32:35 | LL | fn trait_member(val: &T, key: &K) -> bool { | - ^ @@ -22,13 +22,13 @@ LL | fn trait_member(val: &T, key: &K) -> bool { | +++ error[E0220]: associated type `ExpressionStack` not found for `Trait` - --> $DIR/sysroot-private.rs:21:31 + --> $DIR/sysroot-private.rs:22:31 | LL | type AssociatedTy = dyn Trait; | ^^^^^^^^^^^^^^^ there is an associated type `ExpressionStack` in the trait `gimli::read::op::EvaluationStorage` error[E0425]: cannot find function `memchr2` in this scope - --> $DIR/sysroot-private.rs:39:5 + --> $DIR/sysroot-private.rs:40:5 | LL | memchr2(b'a', b'b', buf) | ^^^^^^^ not found in this scope From e586382febf7d03ac330595dcf578184fccbd971 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 29 Jan 2025 02:14:25 +0900 Subject: [PATCH 214/342] Support clobber_abi in BPF inline assembly --- compiler/rustc_target/src/asm/mod.rs | 13 ++++++++++++ tests/codegen/asm/bpf-clobbers.rs | 31 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/codegen/asm/bpf-clobbers.rs diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 1292f46f0c91..f17452b3ba03 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -934,6 +934,7 @@ pub enum InlineAsmClobberAbi { LoongArch, PowerPC, S390x, + Bpf, Msp430, } @@ -1003,6 +1004,10 @@ impl InlineAsmClobberAbi { "C" | "system" => Ok(InlineAsmClobberAbi::S390x), _ => Err(&["C", "system"]), }, + InlineAsmArch::Bpf => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::Bpf), + _ => Err(&["C", "system"]), + }, InlineAsmArch::Msp430 => match name { "C" | "system" => Ok(InlineAsmClobberAbi::Msp430), _ => Err(&["C", "system"]), @@ -1278,6 +1283,14 @@ impl InlineAsmClobberAbi { a8, a9, a10, a11, a12, a13, a14, a15, } }, + InlineAsmClobberAbi::Bpf => clobbered_regs! { + Bpf BpfInlineAsmReg { + // Refs: Section 1.1 "Registers and calling convention" in BPF ABI Recommended Conventions and Guidelines v1.0 + // https://www.kernel.org/doc/html/latest/bpf/standardization/abi.html#registers-and-calling-convention + + r0, r1, r2, r3, r4, r5, + } + }, InlineAsmClobberAbi::Msp430 => clobbered_regs! { Msp430 Msp430InlineAsmReg { r11, r12, r13, r14, r15, diff --git a/tests/codegen/asm/bpf-clobbers.rs b/tests/codegen/asm/bpf-clobbers.rs new file mode 100644 index 000000000000..1117549b1ec3 --- /dev/null +++ b/tests/codegen/asm/bpf-clobbers.rs @@ -0,0 +1,31 @@ +//@ add-core-stubs +//@ compile-flags: --target bpfel-unknown-none +//@ needs-llvm-components: bpf + +#![crate_type = "rlib"] +#![feature(no_core, asm_experimental_arch)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK-LABEL: @flags_clobber +// CHECK: call void asm sideeffect "", ""() +#[no_mangle] +pub unsafe fn flags_clobber() { + asm!("", options(nostack, nomem)); +} + +// CHECK-LABEL: @no_clobber +// CHECK: call void asm sideeffect "", ""() +#[no_mangle] +pub unsafe fn no_clobber() { + asm!("", options(nostack, nomem, preserves_flags)); +} + +// CHECK-LABEL: @clobber_abi +// CHECK: asm sideeffect "", "={r0},={r1},={r2},={r3},={r4},={r5}"() +#[no_mangle] +pub unsafe fn clobber_abi() { + asm!("", clobber_abi("C"), options(nostack, nomem, preserves_flags)); +} From 7e68422859f2e6e3514a1af68d0a7ba3629e2553 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 28 Jan 2025 17:52:28 +0000 Subject: [PATCH 215/342] Properly check that array length is valid type during built-in unsizing in index --- compiler/rustc_hir_typeck/src/place_op.rs | 11 +++++++++-- compiler/rustc_middle/src/traits/mod.rs | 3 +++ .../src/error_reporting/traits/suggestions.rs | 3 +++ .../rustc_trait_selection/src/traits/wf.rs | 2 +- tests/crashes/131103.rs | 6 ------ .../const-generics/bad-subst-const-kind.stderr | 2 ++ .../generic_const_exprs/type_mismatch.stderr | 2 ++ .../issues/index_array_bad_type.rs | 13 +++++++++++++ .../issues/index_array_bad_type.stderr | 18 ++++++++++++++++++ tests/ui/const-generics/transmute-fail.stderr | 4 ++++ tests/ui/const-generics/type_mismatch.stderr | 2 ++ .../consts/bad-array-size-in-type-err.stderr | 4 ++++ ...const-in-impl-fn-return-type.current.stderr | 2 ++ 13 files changed, 63 insertions(+), 9 deletions(-) delete mode 100644 tests/crashes/131103.rs create mode 100644 tests/ui/const-generics/issues/index_array_bad_type.rs create mode 100644 tests/ui/const-generics/issues/index_array_bad_type.stderr diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba6350711357..e1bd9ae2e672 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,6 +1,7 @@ use rustc_errors::Applicability; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::InferOk; +use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, @@ -136,8 +137,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut self_ty = adjusted_ty; if unsize { // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind() { - self_ty = Ty::new_slice(self.tcx, *element_ty); + if let ty::Array(element_ty, ct) = *adjusted_ty.kind() { + self.register_predicate(Obligation::new( + self.tcx, + self.cause(base_expr.span, ObligationCauseCode::ArrayLen(adjusted_ty)), + self.param_env, + ty::ClauseKind::ConstArgHasType(ct, self.tcx.types.usize), + )); + self_ty = Ty::new_slice(self.tcx, element_ty); } else { continue; } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 55d78e083e07..8a9110f842a9 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -194,6 +194,9 @@ pub enum ObligationCauseCode<'tcx> { /// A slice or array is WF only if `T: Sized`. SliceOrArrayElem, + /// An array `[T; N]` can only be indexed (and is only well-formed if) `N` has type usize. + ArrayLen(Ty<'tcx>), + /// A tuple is WF only if its middle elements are `Sized`. TupleElem, 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 471105773e2b..67c870f387eb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2770,6 +2770,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } + ObligationCauseCode::ArrayLen(array_ty) => { + err.note(format!("the length of array `{array_ty}` must be type `usize`")); + } ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9d32eb053860..20b675bcb76b 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -689,7 +689,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); // Note that the len being WF is implicitly checked while visiting. // Here we just check that it's of type usize. - let cause = self.cause(ObligationCauseCode::Misc); + let cause = self.cause(ObligationCauseCode::ArrayLen(t)); self.out.push(traits::Obligation::with_depth( tcx, cause, diff --git a/tests/crashes/131103.rs b/tests/crashes/131103.rs deleted file mode 100644 index 70193e8b3bd3..000000000000 --- a/tests/crashes/131103.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #131103 -struct Struct(pub [u8; N]); - -pub fn function(value: Struct<3>) -> u8 { - value.0[0] -} diff --git a/tests/ui/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index 5c8d9c903635..b36052696425 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.stderr +++ b/tests/ui/const-generics/bad-subst-const-kind.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] { | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `13` is not of type `u64` --> $DIR/bad-subst-const-kind.rs:13:24 diff --git a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr index e03580ec007c..7cb67252da52 100644 --- a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr +++ b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] {} | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error[E0046]: not all trait items implemented, missing: `ASSOC` --> $DIR/type_mismatch.rs:8:1 diff --git a/tests/ui/const-generics/issues/index_array_bad_type.rs b/tests/ui/const-generics/issues/index_array_bad_type.rs new file mode 100644 index 000000000000..91b89cd3fff3 --- /dev/null +++ b/tests/ui/const-generics/issues/index_array_bad_type.rs @@ -0,0 +1,13 @@ +struct Struct(pub [u8; N]); +//~^ ERROR the constant `N` is not of type `usize` + +pub fn function(value: Struct<3>) -> u8 { + value.0[0] + //~^ ERROR the constant `3` is not of type `usize` + + // FIXME(const_generics): Ideally we wouldn't report the above error + // b/c `Struct<_>` is never well formed, but I'd rather report too many + // errors rather than ICE the compiler. +} + +fn main() {} diff --git a/tests/ui/const-generics/issues/index_array_bad_type.stderr b/tests/ui/const-generics/issues/index_array_bad_type.stderr new file mode 100644 index 000000000000..ceea09733776 --- /dev/null +++ b/tests/ui/const-generics/issues/index_array_bad_type.stderr @@ -0,0 +1,18 @@ +error: the constant `N` is not of type `usize` + --> $DIR/index_array_bad_type.rs:1:34 + | +LL | struct Struct(pub [u8; N]); + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; N]` must be type `usize` + +error: the constant `3` is not of type `usize` + --> $DIR/index_array_bad_type.rs:5:5 + | +LL | value.0[0] + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; 3]` must be type `usize` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 978a9744e88a..0e26daa3a0f1 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -3,6 +3,8 @@ error: the constant `W` is not of type `usize` | LL | fn bar(v: [[u32; H]; W]) -> [[u32; W]; H] { | ^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:11:9 @@ -18,6 +20,8 @@ error: the constant `W` is not of type `usize` | LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:26:9 diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index d1bb5c1242f0..bd169ed2ec8f 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | fn bar() -> [u8; N] {} | ^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `N` is not of type `u8` --> $DIR/type_mismatch.rs:2:11 diff --git a/tests/ui/consts/bad-array-size-in-type-err.stderr b/tests/ui/consts/bad-array-size-in-type-err.stderr index 25d14d80c3ec..c3ff216432ee 100644 --- a/tests/ui/consts/bad-array-size-in-type-err.stderr +++ b/tests/ui/consts/bad-array-size-in-type-err.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | arr: [i32; N], | ^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; N]` must be type `usize` error[E0308]: mismatched types --> $DIR/bad-array-size-in-type-err.rs:7:38 @@ -15,6 +17,8 @@ error: the constant `2` is not of type `usize` | LL | let _ = BadArraySize::<2> { arr: [0, 0, 0] }; | ^^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; 2]` must be type `usize` error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr index 1bcc0dbaf672..92ad83c33000 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr @@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize` | LL | fn func() -> [(); N]; | ^^^^^^^ expected `usize`, found `u32` + | + = note: the length of array `[(); N]` must be type `usize` error: aborting due to 2 previous errors From 336a259451730cf7167a78ee91be96e12657da83 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 28 Jan 2025 19:14:51 +0100 Subject: [PATCH 216/342] Bump nightly version -> 2025-01-28 --- clippy_utils/README.md | 2 +- rust-toolchain | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index c267b804124a..251e3dfe41be 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-01-09 +nightly-2025-01-28 ``` diff --git a/rust-toolchain b/rust-toolchain index b1f0a82b1f47..c15d1fe6cd34 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-01-09" +channel = "nightly-2025-01-28" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 3026545ab50e65fcd1c888b77272032000e36147 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 23 Jan 2025 10:16:08 +0100 Subject: [PATCH 217/342] parse_format optimize import use --- compiler/rustc_builtin_macros/src/asm.rs | 2 +- compiler/rustc_builtin_macros/src/format.rs | 2 +- compiler/rustc_parse_format/src/lib.rs | 41 ++++++++----------- compiler/rustc_parse_format/src/tests.rs | 18 ++++---- .../traits/on_unimplemented.rs | 4 +- .../crates/hir-def/src/body/lower/asm.rs | 2 +- .../crates/hir-def/src/hir/format_args.rs | 2 +- 7 files changed, 33 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 5062cf55bb9a..eb5b345e49ec 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -651,7 +651,7 @@ fn expand_preparsed_asm( .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))); for piece in unverified_pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into())) } parse::Piece::NextArgument(arg) => { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5202fe26c401..a0ab6375a666 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -406,7 +406,7 @@ fn make_format_args( for piece in &pieces { match *piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 5418f054bebd..09c88e7f83bb 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -16,11 +16,8 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -use std::{iter, str, string}; - pub use Alignment::*; pub use Count::*; -pub use Piece::*; pub use Position::*; use rustc_lexer::unescape; @@ -86,7 +83,7 @@ impl InnerOffset { #[derive(Clone, Debug, PartialEq)] pub enum Piece<'a> { /// A literal string which should directly be emitted - String(&'a str), + Lit(&'a str), /// This describes that formatting should process the next argument (as /// specified inside) for emission. NextArgument(Box>), @@ -205,11 +202,11 @@ pub enum Count<'a> { } pub struct ParseError { - pub description: string::String, - pub note: Option, - pub label: string::String, + pub description: String, + pub note: Option, + pub label: String, pub span: InnerSpan, - pub secondary_label: Option<(string::String, InnerSpan)>, + pub secondary_label: Option<(String, InnerSpan)>, pub suggestion: Suggestion, } @@ -225,7 +222,7 @@ pub enum Suggestion { /// `format!("{foo:?#}")` -> `format!("{foo:#?}")` /// `format!("{foo:?x}")` -> `format!("{foo:x?}")` /// `format!("{foo:?X}")` -> `format!("{foo:X?}")` - ReorderFormatParameter(InnerSpan, string::String), + ReorderFormatParameter(InnerSpan, String), } /// The parser structure for interpreting the input format string. This is @@ -237,7 +234,7 @@ pub enum Suggestion { pub struct Parser<'a> { mode: ParseMode, input: &'a str, - cur: iter::Peekable>, + cur: std::iter::Peekable>, /// Error messages accumulated during parsing pub errors: Vec, /// Current position of implicit positional argument pointer @@ -278,7 +275,7 @@ impl<'a> Iterator for Parser<'a> { if self.consume('{') { self.last_opening_brace = curr_last_brace; - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let arg = self.argument(lbrace_end); if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { @@ -299,13 +296,13 @@ impl<'a> Iterator for Parser<'a> { _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } - Some(NextArgument(Box::new(arg))) + Some(Piece::NextArgument(Box::new(arg))) } } '}' => { self.cur.next(); if self.consume('}') { - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let err_pos = self.to_span_index(pos); self.err_with_note( @@ -317,7 +314,7 @@ impl<'a> Iterator for Parser<'a> { None } } - _ => Some(String(self.string(pos))), + _ => Some(Piece::Lit(self.string(pos))), } } else { if self.is_source_literal { @@ -336,7 +333,7 @@ impl<'a> Parser<'a> { pub fn new( s: &'a str, style: Option, - snippet: Option, + snippet: Option, append_newline: bool, mode: ParseMode, ) -> Parser<'a> { @@ -366,7 +363,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err, S2: Into>( + fn err, S2: Into>( &mut self, description: S1, label: S2, @@ -385,11 +382,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note< - S1: Into, - S2: Into, - S3: Into, - >( + fn err_with_note, S2: Into, S3: Into>( &mut self, description: S1, label: S2, @@ -968,7 +961,7 @@ impl<'a> Parser<'a> { /// in order to properly synthesise the intra-string `Span`s for error diagnostics. fn find_width_map_from_snippet( input: &str, - snippet: Option, + snippet: Option, str_style: Option, ) -> InputStringKind { let snippet = match snippet { @@ -1083,8 +1076,8 @@ fn find_width_map_from_snippet( InputStringKind::Literal { width_mappings } } -fn unescape_string(string: &str) -> Option { - let mut buf = string::String::new(); +fn unescape_string(string: &str) -> Option { + let mut buf = String::new(); let mut ok = true; unescape::unescape_unicode(string, unescape::Mode::Str, &mut |_, unescaped_char| { match unescaped_char { diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 81e5bca0ba9f..fbb217b16fc3 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -1,3 +1,5 @@ +use Piece::*; + use super::*; #[track_caller] @@ -32,12 +34,12 @@ fn musterr(s: &str) { #[test] fn simple() { - same("asdf", &[String("asdf")]); - same("a{{b", &[String("a"), String("{b")]); - same("a}}b", &[String("a"), String("}b")]); - same("a}}", &[String("a"), String("}")]); - same("}}", &[String("}")]); - same("\\}}", &[String("\\"), String("}")]); + same("asdf", &[Lit("asdf")]); + same("a{{b", &[Lit("a"), Lit("{b")]); + same("a}}b", &[Lit("a"), Lit("}b")]); + same("a}}", &[Lit("a"), Lit("}")]); + same("}}", &[Lit("}")]); + same("\\}}", &[Lit("\\"), Lit("}")]); } #[test] @@ -370,7 +372,7 @@ fn format_flags() { #[test] fn format_mixture() { same("abcd {3:x} efg", &[ - String("abcd "), + Lit("abcd "), NextArgument(Box::new(Argument { position: ArgumentIs(3), position_span: InnerSpan { start: 7, end: 8 }, @@ -390,7 +392,7 @@ fn format_mixture() { ty_span: None, }, })), - String(" efg"), + Lit(" efg"), ]); } #[test] diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 4e0b097db4c6..3d79b0acf83a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -799,7 +799,7 @@ impl<'tcx> OnUnimplementedFormatString { let mut result = Ok(()); for token in &mut parser { match token { - Piece::String(_) => (), // Normal string, no need to check it + Piece::Lit(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => { let format_spec = a.format; if self.is_diagnostic_namespace_variant @@ -950,7 +950,7 @@ impl<'tcx> OnUnimplementedFormatString { let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); let constructed_message = (&mut parser) .map(|p| match p { - Piece::String(s) => s.to_owned(), + Piece::Lit(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { Position::ArgumentNamed(arg) => { let s = Symbol::intern(arg); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index 68c7173d1e40..994ba2aa069d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -229,7 +229,7 @@ impl ExprCollector<'_> { }; for piece in unverified_pieces { match piece { - rustc_parse_format::Piece::String(_) => {} + rustc_parse_format::Piece::Lit(_) => {} rustc_parse_format::Piece::NextArgument(arg) => { // let span = arg_spans.next(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index e64e498c1707..28c824fd31d7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -287,7 +287,7 @@ pub(crate) fn parse( for piece in pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(arg) => { From 2b8930c71c8040d338f99e1b1be6f1056edd6638 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 25 Jan 2025 03:03:39 +0000 Subject: [PATCH 218/342] Consolidate OutlivesEnv construction with resolve_regions --- .../src/region_infer/opaque_types.rs | 2 +- .../rustc_hir_analysis/src/check/check.rs | 5 +-- .../src/check/compare_impl_item.rs | 24 +++------- .../src/check/compare_impl_item/refine.rs | 8 +--- .../rustc_hir_analysis/src/check/dropck.rs | 3 +- compiler/rustc_hir_analysis/src/check/mod.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 21 +++++---- .../src/coherence/builtin.rs | 7 +-- .../src/impl_wf_check/min_specialization.rs | 8 +--- .../rustc_lint/src/impl_trait_overcaptures.rs | 2 +- compiler/rustc_trait_selection/src/regions.rs | 18 +++++++- .../src/traits/coherence.rs | 6 +-- .../src/traits/engine.rs | 16 ++++--- .../rustc_trait_selection/src/traits/misc.rs | 23 +--------- .../rustc_trait_selection/src/traits/mod.rs | 4 +- .../src/traits/outlives_bounds.rs | 45 ++++++++----------- 16 files changed, 75 insertions(+), 121 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 7c484327e311..1e7451a16afc 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -439,7 +439,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); Default::default() }); - let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys); + let implied_bounds = infcx.implied_bounds_tys(parent, param_env, wf_tys); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let mut seen = vec![tcx.lifetimes.re_static]; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index b0a6922ff72b..40049f96de4a 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -27,7 +27,6 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_type_ir::fold::TypeFoldable; use tracing::{debug, instrument}; use ty::TypingMode; @@ -417,9 +416,7 @@ fn check_opaque_meets_bounds<'tcx>( } let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?; - let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; + ocx.resolve_regions_and_report_errors(defining_use_anchor, param_env, wf_tys)?; if infcx.next_trait_solver() { Ok(()) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index d2ab98bae891..7332888c5f91 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -9,7 +9,6 @@ use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_e use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -24,7 +23,6 @@ use rustc_span::Span; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, }; @@ -416,11 +414,7 @@ fn compare_method_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(impl_m_def_id, param_env, wf_tys); if !errors.is_empty() { return Err(infcx .tainted_by_errors() @@ -725,11 +719,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys), - ); - ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; + ocx.resolve_regions_and_report_errors(impl_m_def_id, param_env, wf_tys)?; let mut remapped_types = DefIdMap::default(); for (def_id, (ty, args)) in collected_types { @@ -1883,8 +1873,7 @@ fn compare_const_predicate_entailment<'tcx>( return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } - let outlives_env = OutlivesEnvironment::new(param_env); - ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ct_def_id, param_env, []) } #[instrument(level = "debug", skip(tcx))] @@ -2017,8 +2006,7 @@ fn compare_type_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_env = OutlivesEnvironment::new(param_env); - ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, []) } /// Validate that `ProjectionCandidate`s created for this associated type will @@ -2147,9 +2135,7 @@ pub(super) fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, &assumed_wf_types); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, assumed_wf_types) } struct ReplaceTy<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 2b14594ea1bf..494e9560c815 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; @@ -13,7 +12,6 @@ use rustc_middle::ty::{ }; use rustc_span::Span; use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCtxt, elaborate, normalize_param_env_or_error}; /// Check that an implementation does not refine an RPITIT from a trait method signature. @@ -170,11 +168,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>( tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)"); return; } - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), &implied_wf_types), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(impl_m.def_id.expect_local(), param_env, implied_wf_types); if !errors.is_empty() { tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)"); return; diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 1c9bbe627fb0..0876bb828485 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::util::CheckRegions; @@ -192,7 +191,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( return Err(guar.unwrap()); } - let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(adt_env)); + let errors = ocx.infcx.resolve_regions(adt_def_id, adt_env, []); if !errors.is_empty() { let mut guar = None; for error in errors { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 69b4aa47ebad..c6ec27209602 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -80,7 +80,6 @@ use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::DenseBitSet; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; use rustc_middle::query::Providers; @@ -655,8 +654,7 @@ pub fn check_function_signature<'tcx>( } } - let outlives_env = OutlivesEnvironment::new(param_env); - if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, &outlives_env) { + if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, param_env, []) { return Err(e); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index cd19993f9378..5023a09e2f38 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -128,13 +128,17 @@ where let infcx_compat = infcx.fork(); // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. - let implied_bounds = - infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); + let implied_bounds = infcx.implied_bounds_tys_compat( + body_def_id, + param_env, + assumed_wf_types.iter().copied(), + false, + ); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions_with_outlives_env(&outlives_env); if errors.is_empty() { return Ok(()); } @@ -173,9 +177,9 @@ where // just obscures what we mean here anyways. Let's just be explicit. if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { let implied_bounds = - infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true); + infcx_compat.implied_bounds_tys_compat(body_def_id, param_env, assumed_wf_types, true); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - let errors_compat = infcx_compat.resolve_regions(&outlives_env); + let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env); if errors_compat.is_empty() { Ok(()) } else { @@ -769,12 +773,7 @@ fn test_region_obligations<'tcx>( add_constraints(&infcx); - let outlives_environment = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, id, wf_tys), - ); - - let errors = infcx.resolve_regions(&outlives_environment); + let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied()); debug!(?errors, "errors"); // If we were able to prove that the type outlives the region without diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 27a7c2ea530f..6bbd76633da8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -10,7 +10,6 @@ use rustc_hir as hir; use rustc_hir::ItemKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; @@ -346,8 +345,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - res = res.and(ocx.resolve_regions_and_report_errors(impl_did, &outlives_env)); + res = res.and(ocx.resolve_regions_and_report_errors(impl_did, param_env, [])); } res } @@ -564,8 +562,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( } // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env); + let _ = ocx.resolve_regions_and_report_errors(impl_did, param_env, []); Ok(CoerceUnsizedInfo { custom_kind: kind }) } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index ee55e1bc21a6..32694d5f4bc1 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -68,7 +68,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::specialization_graph::Node; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -77,7 +76,6 @@ use rustc_middle::ty::{ }; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt, translate_args_with_cause, wf}; use tracing::{debug, instrument}; @@ -176,7 +174,6 @@ fn get_impl_args( let ocx = ObligationCtxt::new_with_diagnostics(infcx); let param_env = tcx.param_env(impl1_def_id); let impl1_span = tcx.def_span(impl1_def_id); - let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?; let impl1_args = GenericArgs::identity_for_item(tcx, impl1_def_id); let impl2_args = translate_args_with_cause( @@ -194,9 +191,8 @@ fn get_impl_args( return Err(guar); } - let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, &assumed_wf_types); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); + let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?; + let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, param_env, assumed_wf_types); let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else { let span = tcx.def_span(impl1_def_id); let guar = tcx.dcx().emit_err(GenericArgsOnOverriddenImpl { span }); diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 44f865355271..d6546066d86c 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -191,7 +191,7 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { let ocx = ObligationCtxt::new(&infcx); let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); let implied_bounds = - infcx.implied_bounds_tys_compat(param_env, parent_def_id, &assumed_wf_tys, false); + infcx.implied_bounds_tys_compat(parent_def_id, param_env, assumed_wf_tys, false); OutlivesEnvironment::with_bounds(param_env, implied_bounds) }), }); diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 863b6e293ff8..1bae6c80cfac 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,10 +1,13 @@ +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{self, Ty}; use crate::traits::ScrubbedTraitError; +use crate::traits::outlives_bounds::InferCtxtExt; #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { @@ -15,10 +18,23 @@ impl<'tcx> InferCtxt<'tcx> { /// Prefer this method over `resolve_regions_with_normalize`, unless you are /// doing something specific for normalization. fn resolve_regions( + &self, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, + ) -> Vec> { + self.resolve_regions_with_outlives_env(&OutlivesEnvironment::with_bounds( + param_env, + self.implied_bounds_tys(body_id, param_env, assumed_wf_tys), + )) + } + + /// Don't call this directly unless you know what you're doing. + fn resolve_regions_with_outlives_env( &self, outlives_env: &OutlivesEnvironment<'tcx>, ) -> Vec> { - self.resolve_regions_with_normalize(outlives_env, |ty, origin| { + self.resolve_regions_with_normalize(&outlives_env, |ty, origin| { let ty = self.resolve_vars_if_possible(ty); if self.next_trait_solver() { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 50d47d20e1a4..7ee9eb453098 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -9,7 +9,7 @@ use std::fmt::Debug; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::PredicateObligations; use rustc_middle::bug; @@ -27,7 +27,6 @@ use tracing::{debug, instrument, warn}; use super::ObligationCtxt; use crate::error_reporting::traits::suggest_new_overflow_limit; use crate::infer::InferOk; -use crate::infer::outlives::env::OutlivesEnvironment; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect}; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -596,8 +595,7 @@ fn try_prove_negated_where_clause<'tcx>( // FIXME: We could use the assumed_wf_types from both impls, I think, // if that wasn't implemented just for LocalDefId, and we'd need to do // the normalization ourselves since this is totally fallible... - let outlives_env = OutlivesEnvironment::new(param_env); - let errors = ocx.resolve_regions(&outlives_env); + let errors = ocx.resolve_regions(CRATE_DEF_ID, param_env, []); if !errors.is_empty() { return false; } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 4a3983fca31c..9f3178f88792 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -8,7 +8,6 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_infer::traits::PredicateObligations; use rustc_macros::extension; @@ -217,14 +216,15 @@ where /// will result in region constraints getting ignored. pub fn resolve_regions_and_report_errors( self, - generic_param_scope: LocalDefId, - outlives_env: &OutlivesEnvironment<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, ) -> Result<(), ErrorGuaranteed> { - let errors = self.infcx.resolve_regions(outlives_env); + let errors = self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys); if errors.is_empty() { Ok(()) } else { - Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors)) + Err(self.infcx.err_ctxt().report_region_errors(body_id, &errors)) } } @@ -235,9 +235,11 @@ where #[must_use] pub fn resolve_regions( self, - outlives_env: &OutlivesEnvironment<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, ) -> Vec> { - self.infcx.resolve_regions(outlives_env) + self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys) } } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 7a67b943e949..79e178150de3 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -4,13 +4,10 @@ use std::assert_matches::assert_matches; use hir::LangItem; use rustc_ast::Mutability; -use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; -use super::outlives_bounds::InferCtxtExt; use crate::regions::InferCtxtRegionExt; use crate::traits::{self, FulfillmentError, ObligationCause}; @@ -170,15 +167,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( } // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( - param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]); if !errors.is_empty() { infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors))); continue; @@ -261,15 +250,7 @@ pub fn all_fields_implement_trait<'tcx>( } // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( - param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]); if !errors.is_empty() { infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index fe5ad003a7e4..6b5ebade6aed 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -290,12 +290,10 @@ fn do_normalize_predicates<'tcx>( // We can use the `elaborated_env` here; the region code only // cares about declarations like `'a: 'b`. - let outlives_env = OutlivesEnvironment::new(elaborated_env); - // FIXME: It's very weird that we ignore region obligations but apparently // still need to use `resolve_regions` as we need the resolved regions in // the normalized predicates. - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); if !errors.is_empty() { tcx.dcx().span_delayed_bug( span, diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 23dabe32ff2e..eecc499f384d 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,4 +1,3 @@ -use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; @@ -12,9 +11,6 @@ use tracing::instrument; use crate::infer::InferCtxt; use crate::traits::{ObligationCause, ObligationCtxt}; -pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator> + 'a; -pub type Bounds<'a, 'tcx: 'a> = impl Iterator> + 'a; - /// Implied bounds are region relationships that we deduce /// automatically. The idea is that (e.g.) a caller must check that a /// function's argument types are well-formed immediately before @@ -110,36 +106,33 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds } -#[extension(pub trait InferCtxtExt<'a, 'tcx>)] -impl<'a, 'tcx: 'a> InferCtxt<'tcx> { +#[extension(pub trait InferCtxtExt<'tcx>)] +impl<'tcx> InferCtxt<'tcx> { /// Do *NOT* call this directly. - fn implied_bounds_tys_compat( - &'a self, - param_env: ParamEnv<'tcx>, + fn implied_bounds_tys_compat>>( + &self, body_id: LocalDefId, - tys: &'a FxIndexSet>, + param_env: ParamEnv<'tcx>, + tys: Tys, compat: bool, - ) -> BoundsCompat<'a, 'tcx> { - tys.iter() - .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat)) + ) -> impl Iterator> { + tys.into_iter() + .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat)) } /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat` /// with `compat` set to `true`, otherwise `false`. fn implied_bounds_tys( - &'a self, - param_env: ParamEnv<'tcx>, + &self, body_id: LocalDefId, - tys: &'a FxIndexSet>, - ) -> Bounds<'a, 'tcx> { - tys.iter().flat_map(move |ty| { - implied_outlives_bounds( - self, - param_env, - body_id, - *ty, - !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, - ) - }) + param_env: ParamEnv<'tcx>, + tys: impl IntoIterator>, + ) -> impl Iterator> { + self.implied_bounds_tys_compat( + body_id, + param_env, + tys, + !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, + ) } } From 48b7e38c0607e856dbbb932e60ecc85e45a1427d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 25 Jan 2025 04:26:32 +0000 Subject: [PATCH 219/342] Move outlives env computation into methods --- .../src/region_infer/opaque_types.rs | 16 +++---- .../rustc_hir_analysis/src/check/wfcheck.rs | 17 ++++--- .../rustc_infer/src/infer/outlives/env.rs | 6 --- .../rustc_lint/src/impl_trait_overcaptures.rs | 6 +-- compiler/rustc_trait_selection/src/regions.rs | 46 ++++++++++++++++++- .../src/traits/auto_trait.rs | 4 +- .../src/traits/outlives_bounds.rs | 21 ++------- .../src/traits/implied-bounds.md | 4 +- 8 files changed, 71 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 1e7451a16afc..5440d451ec8d 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,6 +1,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::OpaqueTyOrigin; use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; use rustc_macros::extension; use rustc_middle::ty::fold::fold_regions; @@ -10,6 +12,7 @@ use rustc_middle::ty::{ TypingMode, }; use rustc_span::Span; +use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; @@ -406,10 +409,6 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { } fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { - use rustc_hir as hir; - use rustc_infer::infer::outlives::env::OutlivesEnvironment; - use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; - if let Some(&canonical_args) = self.canonical_args.get() { return canonical_args; } @@ -417,9 +416,9 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let &Self { tcx, def_id, .. } = self; let origin = tcx.local_opaque_ty_origin(def_id); let parent = match origin { - hir::OpaqueTyOrigin::FnReturn { parent, .. } - | hir::OpaqueTyOrigin::AsyncFn { parent, .. } - | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, + OpaqueTyOrigin::FnReturn { parent, .. } + | OpaqueTyOrigin::AsyncFn { parent, .. } + | OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(parent); let args = GenericArgs::identity_for_item(tcx, parent).extend_to( @@ -439,8 +438,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); Default::default() }); - let implied_bounds = infcx.implied_bounds_tys(parent, param_env, wf_tys); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys); let mut seen = vec![tcx.lifetimes.re_static]; let canonical_args = fold_regions(tcx, args, |r1, _| { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5023a09e2f38..83bb8c0dfaf1 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -25,11 +25,10 @@ use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::regions::InferCtxtRegionExt; +use rustc_trait_selection::regions::{InferCtxtRegionExt, OutlivesEnvironmentBuildExt}; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty, }; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, @@ -128,13 +127,13 @@ where let infcx_compat = infcx.fork(); // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. - let implied_bounds = infcx.implied_bounds_tys_compat( + let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( + &infcx, body_def_id, param_env, assumed_wf_types.iter().copied(), false, ); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); @@ -176,9 +175,13 @@ where // but that does result in slightly more work when this option is set and // just obscures what we mean here anyways. Let's just be explicit. if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - let implied_bounds = - infcx_compat.implied_bounds_tys_compat(body_def_id, param_env, assumed_wf_types, true); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( + &infcx, + body_def_id, + param_env, + assumed_wf_types, + true, + ); let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env); if errors_compat.is_empty() { Ok(()) diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 9300fc574dc2..f8ab0b53a005 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -59,12 +59,6 @@ pub struct OutlivesEnvironment<'tcx> { pub type RegionBoundPairs<'tcx> = FxIndexSet>>; impl<'tcx> OutlivesEnvironment<'tcx> { - /// Create a new `OutlivesEnvironment` without extra outlives bounds. - #[inline] - pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { - Self::with_bounds(param_env, vec![]) - } - /// Create a new `OutlivesEnvironment` with extra outlives bounds. pub fn with_bounds( param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index d6546066d86c..d251b4b7459e 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -25,8 +25,8 @@ use rustc_span::{Span, Symbol}; use rustc_trait_selection::errors::{ AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion, }; +use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::traits::ObligationCtxt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; use crate::{LateContext, LateLintPass, fluent_generated as fluent}; @@ -190,9 +190,7 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); let ocx = ObligationCtxt::new(&infcx); let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); - let implied_bounds = - infcx.implied_bounds_tys_compat(parent_def_id, param_env, assumed_wf_tys, false); - OutlivesEnvironment::with_bounds(param_env, implied_bounds) + OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys) }), }); } diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 1bae6c80cfac..fc9fa44b4c61 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -9,6 +9,46 @@ use rustc_middle::ty::{self, Ty}; use crate::traits::ScrubbedTraitError; use crate::traits::outlives_bounds::InferCtxtExt; +#[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] +impl<'tcx> OutlivesEnvironment<'tcx> { + fn new( + infcx: &InferCtxt<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, + ) -> Self { + Self::new_with_implied_bounds_compat( + infcx, + body_id, + param_env, + assumed_wf_tys, + !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, + ) + } + + fn new_with_implied_bounds_compat( + infcx: &InferCtxt<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, + implied_bounds_compat: bool, + ) -> Self { + // FIXME: This needs to be modified so that we normalize the known type + // outlives obligations then elaborate them into their region/type components. + // Otherwise, ` as Mirror>::Assoc: 'b` will not imply `'a: 'b` even + // if we can normalize `'a`. + OutlivesEnvironment::with_bounds( + param_env, + infcx.implied_bounds_tys_with_compat( + body_id, + param_env, + assumed_wf_tys, + implied_bounds_compat, + ), + ) + } +} + #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { /// Resolve regions, using the deep normalizer to normalize any type-outlives @@ -23,9 +63,11 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, assumed_wf_tys: impl IntoIterator>, ) -> Vec> { - self.resolve_regions_with_outlives_env(&OutlivesEnvironment::with_bounds( + self.resolve_regions_with_outlives_env(&OutlivesEnvironment::new( + self, + body_id, param_env, - self.implied_bounds_tys(body_id, param_env, assumed_wf_tys), + assumed_wf_tys, )) } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 9a53e8a5d519..1fca2f4da7ee 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,6 +6,7 @@ use std::iter; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::unord::UnordSet; +use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::ty::{Region, RegionVid}; use tracing::debug; @@ -13,6 +14,7 @@ use tracing::debug; use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; +use crate::regions::OutlivesEnvironmentBuildExt; use crate::traits::project::ProjectAndUnifyResult; // FIXME(twk): this is obviously not nice to duplicate like that @@ -158,7 +160,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}"); } - let outlives_env = OutlivesEnvironment::new(full_env); + let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty)); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index eecc499f384d..189326958078 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -108,8 +108,9 @@ fn implied_outlives_bounds<'a, 'tcx>( #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { - /// Do *NOT* call this directly. - fn implied_bounds_tys_compat>>( + /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment` + /// instead if you're interested in the implied bounds for a given signature. + fn implied_bounds_tys_with_compat>>( &self, body_id: LocalDefId, param_env: ParamEnv<'tcx>, @@ -119,20 +120,4 @@ impl<'tcx> InferCtxt<'tcx> { tys.into_iter() .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat)) } - - /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat` - /// with `compat` set to `true`, otherwise `false`. - fn implied_bounds_tys( - &self, - body_id: LocalDefId, - param_env: ParamEnv<'tcx>, - tys: impl IntoIterator>, - ) -> impl Iterator> { - self.implied_bounds_tys_compat( - body_id, - param_env, - tys, - !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, - ) - } } diff --git a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md index 63b09a43f476..05693dcd5a18 100644 --- a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md +++ b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md @@ -40,7 +40,7 @@ requirements of impls and functions as explicit predicates. ### using implicit implied bounds as assumptions These bounds are not added to the `ParamEnv` of the affected item itself. For lexical -region resolution they are added using [`fn OutlivesEnvironment::with_bounds`]. +region resolution they are added using [`fn OutlivesEnvironment::new`]. Similarly, during MIR borrowck we add them using [`fn UniversalRegionRelationsBuilder::add_implied_bounds`]. @@ -55,7 +55,7 @@ The assumed outlives constraints for implicit bounds are computed using the MIR borrowck adds the outlives constraints for both the normalized and unnormalized types, lexical region resolution [only uses the unnormalized types][notnorm]. -[`fn OutlivesEnvironment::with_bounds`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_infer/src/infer/outlives/env.rs#L90-L97 +[`fn OutlivesEnvironment::new`]: TODO [`fn UniversalRegionRelationsBuilder::add_implied_bounds`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs#L316 [mir]: https://github.com/rust-lang/rust/blob/91cae1dcdcf1a31bd8a92e4a63793d65cfe289bb/compiler/rustc_borrowck/src/type_check/free_region_relations.rs#L258-L332 [`fn assumed_wf_types`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_ty_utils/src/implied_bounds.rs#L21 From 6fb07164c202592831b8bd42f501be104a856213 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 28 Jan 2025 11:01:48 -0800 Subject: [PATCH 220/342] Update mdbook to 0.4.44 Updates to mdbook 0.4.44. Changelog: https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md#mdbook-0444 --- src/tools/rustbook/Cargo.lock | 52 +++++++++++++++++------------------ src/tools/rustbook/Cargo.toml | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 86d2abcacb7f..b31bf61a6fba 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -191,9 +191,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.26" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", "clap_derive", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", @@ -214,9 +214,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" +checksum = "0952013545c9c6dca60f491602655b795c6c062ab180c9cb0bccb83135461861" dependencies = [ "clap", ] @@ -253,9 +253,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -750,9 +750,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -867,9 +867,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe1f98b8d66e537d2f0ba06e7dec4f44001deec539a2d18bfc102d6a86189148" +checksum = "f9da1e54401fe5d45a664c57e112e70f18e8c5a73e268c179305b932ee864574" dependencies = [ "ammonia", "anyhow", @@ -1372,9 +1372,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", "errno", @@ -1391,9 +1391,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -1412,9 +1412,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" @@ -1438,9 +1438,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -1732,9 +1732,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-width" @@ -1972,9 +1972,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index c2ce8fef4d0b..9f9846cdee07 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -14,6 +14,6 @@ mdbook-i18n-helpers = "0.3.3" mdbook-spec = { path = "../../doc/reference/mdbook-spec" } [dependencies.mdbook] -version = "0.4.37" +version = "0.4.44" default-features = false features = ["search"] From 3f8ce7c973e57c99e51df0c94ab3440be9580315 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Jan 2025 21:07:10 +0000 Subject: [PATCH 221/342] Do not assume child bound assumptions for rigid alias --- compiler/rustc_middle/src/ty/context.rs | 14 +++++ .../src/solve/assembly/mod.rs | 63 ++++++++++++++----- compiler/rustc_type_ir/src/interner.rs | 10 +++ ...opy-bound-from-child-rigid.current.stderr} | 2 +- ...ee-copy-bound-from-child-rigid.next.stderr | 14 +++++ .../cant-see-copy-bound-from-child-rigid.rs | 4 ++ .../const-traits/const-impl-trait.stderr | 52 ++++++++------- 7 files changed, 123 insertions(+), 36 deletions(-) rename tests/ui/associated-type-bounds/{cant-see-copy-bound-from-child-rigid.stderr => cant-see-copy-bound-from-child-rigid.current.stderr} (88%) create mode 100644 tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1f0710e24152..da4a86814820 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -345,6 +345,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.item_bounds(def_id).map_bound(IntoIterator::into_iter) } + fn item_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_super_predicates(def_id).map_bound(IntoIterator::into_iter) + } + + fn item_non_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_non_self_assumptions(def_id).map_bound(IntoIterator::into_iter) + } + fn predicates_of( self, def_id: DefId, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 63432dc199b9..d0b01b14d635 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -19,6 +19,11 @@ use crate::solve::{ MaybeCause, NoSolution, QueryResult, }; +enum AliasBoundKind { + SelfBounds, + NonSelfBounds, +} + /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, @@ -510,7 +515,12 @@ where candidates: &mut Vec>, ) { let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates); + ecx.assemble_alias_bound_candidates_recur( + goal.predicate.self_ty(), + goal, + candidates, + AliasBoundKind::SelfBounds, + ); }); } @@ -528,6 +538,7 @@ where self_ty: I::Ty, goal: Goal, candidates: &mut Vec>, + consider_self_bounds: AliasBoundKind, ) { let (kind, alias_ty) = match self_ty.kind() { ty::Bool @@ -580,16 +591,37 @@ where } }; - for assumption in - self.cx().item_bounds(alias_ty.def_id).iter_instantiated(self.cx(), alias_ty.args) - { - candidates.extend(G::probe_and_consider_implied_clause( - self, - CandidateSource::AliasBound, - goal, - assumption, - [], - )); + match consider_self_bounds { + AliasBoundKind::SelfBounds => { + for assumption in self + .cx() + .item_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } + AliasBoundKind::NonSelfBounds => { + for assumption in self + .cx() + .item_non_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } } candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty)); @@ -600,9 +632,12 @@ where // Recurse on the self type of the projection. match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) { - Ok(next_self_ty) => { - self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates) - } + Ok(next_self_ty) => self.assemble_alias_bound_candidates_recur( + next_self_ty, + goal, + candidates, + AliasBoundKind::NonSelfBounds, + ), Err(NoSolution) => {} } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 4fec606a8315..0c3b0758f0f9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -203,6 +203,16 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>; + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + fn predicates_of( self, def_id: Self::DefId, diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr similarity index 88% rename from tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr rename to tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr index 3ed73918de31..0d57d9d0142d 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/cant-see-copy-bound-from-child-rigid.rs:14:9 + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 | LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr new file mode 100644 index 000000000000..0d57d9d0142d --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs index 6b3fd7e898d1..fe135031b313 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + trait Id { type This: ?Sized; } diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr index 4e3200594485..27d7957c0014 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ b/tests/ui/traits/const-traits/const-impl-trait.stderr @@ -99,26 +99,6 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# --> $SRC_DIR/core/src/cmp.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:23:22 - | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; - | ^^^^^^ can't be applied to `PartialEq` - | -note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:23:22 - | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; - | ^^^^^^ can't be applied to `PartialEq` - | -note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: `~const` can only be applied to `#[const_trait]` traits --> $DIR/const-impl-trait.rs:27:22 | @@ -149,6 +129,36 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# --> $SRC_DIR/core/src/cmp.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0015]: cannot call non-const operator in constants --> $DIR/const-impl-trait.rs:35:13 | @@ -181,7 +191,7 @@ LL | a == a | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0015, E0635. For more information about an error, try `rustc --explain E0015`. From 009d68740f660562c81c8565f5a5e6ec7ccfb4ae Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Jan 2025 21:07:54 +0000 Subject: [PATCH 222/342] Make item self/non-self bound naming less whack --- compiler/rustc_hir_analysis/src/check/mod.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 14 +++--- .../src/collect/item_bounds.rs | 11 +++-- compiler/rustc_hir_typeck/src/closure.rs | 4 +- compiler/rustc_hir_typeck/src/coercion.rs | 44 +++++++++---------- .../rustc_infer/src/infer/outlives/verify.rs | 2 +- compiler/rustc_lint/src/unused.rs | 33 +++++++------- .../src/rmeta/decoder/cstore_impl.rs | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 12 ++--- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 6 +-- compiler/rustc_middle/src/ty/context.rs | 6 +-- .../ty/return_position_impl_trait_in_trait.rs | 2 +- .../src/error_reporting/infer/mod.rs | 2 +- .../error_reporting/infer/note_and_explain.rs | 4 +- .../src/error_reporting/traits/suggestions.rs | 33 +++++++------- .../src/traits/select/mod.rs | 4 +- .../clippy_lints/src/future_not_send.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 6 +-- 19 files changed, 91 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 69b4aa47ebad..cc0b7fdd8dd1 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -457,7 +457,7 @@ fn fn_sig_suggestion<'tcx>( let asyncness = if tcx.asyncness(assoc.def_id).is_async() { output = if let ty::Alias(_, alias_ty) = *output.kind() { - tcx.explicit_item_super_predicates(alias_ty.def_id) + tcx.explicit_item_self_bounds(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) .find_map(|(bound, _)| { bound.as_projection_clause()?.no_bound_vars()?.term.as_type() diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 447050ea7d20..cad7b2a1e57b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -65,9 +65,9 @@ pub fn provide(providers: &mut Providers) { type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, - item_super_predicates: item_bounds::item_super_predicates, - explicit_item_super_predicates: item_bounds::explicit_item_super_predicates, - item_non_self_assumptions: item_bounds::item_non_self_assumptions, + item_self_bounds: item_bounds::item_self_bounds, + explicit_item_self_bounds: item_bounds::explicit_item_self_bounds, + item_non_self_bounds: item_bounds::item_non_self_bounds, impl_super_outlives: item_bounds::impl_super_outlives, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, @@ -328,9 +328,9 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().generics_of(def_id); self.tcx.ensure().predicates_of(def_id); self.tcx.ensure().explicit_item_bounds(def_id); - self.tcx.ensure().explicit_item_super_predicates(def_id); + self.tcx.ensure().explicit_item_self_bounds(def_id); self.tcx.ensure().item_bounds(def_id); - self.tcx.ensure().item_super_predicates(def_id); + self.tcx.ensure().item_self_bounds(def_id); if self.tcx.is_conditionally_const(def_id) { self.tcx.ensure().explicit_implied_const_bounds(def_id); self.tcx.ensure().const_conditions(def_id); @@ -822,7 +822,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, Some(_)) => { tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure().item_self_bounds(def_id); tcx.ensure().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); @@ -839,7 +839,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, None) => { tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure().item_self_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index d3ff1f7bebe6..e37a11b68445 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -350,7 +350,7 @@ pub(super) fn explicit_item_bounds( explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) } -pub(super) fn explicit_item_super_predicates( +pub(super) fn explicit_item_self_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { @@ -434,11 +434,11 @@ pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, }) } -pub(super) fn item_super_predicates( +pub(super) fn item_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { - tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { + tcx.explicit_item_self_bounds(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter( util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), ) @@ -447,13 +447,12 @@ pub(super) fn item_super_predicates( /// This exists as an optimization to compute only the item bounds of the item /// that are not `Self` bounds. -pub(super) fn item_non_self_assumptions( +pub(super) fn item_non_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); - let own_bounds: FxIndexSet<_> = - tcx.item_super_predicates(def_id).skip_binder().iter().collect(); + let own_bounds: FxIndexSet<_> = tcx.item_self_bounds(def_id).skip_binder().iter().collect(); if all_bounds.len() == own_bounds.len() { ty::EarlyBinder::bind(ty::ListWithCachedTypeInfo::empty()) } else { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b8652d82d91b..d569d5f59c9e 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -308,7 +308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty, closure_kind, self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .map(|(c, s)| (c.as_predicate(), s)), ), @@ -981,7 +981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 47abba1cc29c..153fd6001bbc 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1847,30 +1847,26 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.probe(|_| { let ocx = ObligationCtxt::new(fcx); ocx.register_obligations( - fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map( - |clause| { - let predicate = clause - .kind() - .map_bound(|clause| match clause { - ty::ClauseKind::Trait(trait_pred) => Some( - ty::ClauseKind::Trait(trait_pred.with_self_ty(fcx.tcx, ty)), - ), - ty::ClauseKind::Projection(proj_pred) => { - Some(ty::ClauseKind::Projection( - proj_pred.with_self_ty(fcx.tcx, ty), - )) - } - _ => None, - }) - .transpose()?; - Some(Obligation::new( - fcx.tcx, - ObligationCause::dummy(), - fcx.param_env, - predicate, - )) - }, - ), + fcx.tcx.item_self_bounds(rpit_def_id).iter_identity().filter_map(|clause| { + let predicate = clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ClauseKind::Trait( + trait_pred.with_self_ty(fcx.tcx, ty), + )), + ty::ClauseKind::Projection(proj_pred) => Some( + ty::ClauseKind::Projection(proj_pred.with_self_ty(fcx.tcx, ty)), + ), + _ => None, + }) + .transpose()?; + Some(Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + predicate, + )) + }), ); ocx.select_where_possible().is_empty() }) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 7a21c2883d1a..d68f3639176f 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -281,7 +281,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator> { let tcx = self.tcx; - let bounds = tcx.item_super_predicates(alias_ty.def_id); + let bounds = tcx.item_self_bounds(alias_ty.def_id); trace!("{:#?}", bounds.skip_binder()); bounds .iter_instantiated(tcx, alias_ty.args) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3059adb3fda1..5696fcaed137 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -289,25 +289,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { - elaborate( - cx.tcx, - cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(), - ) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; + elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) + is_def_must_use(cx, def_id, span) + } else { + None + } + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) } ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 527f2f10205a..da07ad8f6c07 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -241,7 +241,7 @@ impl IntoArgs for (CrateNum, SimplifiedType) { provide! { tcx, def_id, other, cdata, explicit_item_bounds => { table_defaulted_array } - explicit_item_super_predicates => { table_defaulted_array } + explicit_item_self_bounds => { table_defaulted_array } explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c538ab99fb54..904409dd777e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1554,7 +1554,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { @@ -1667,10 +1667,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); } - fn encode_explicit_item_super_predicates(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_explicit_item_super_predicates({:?})", def_id); - let bounds = self.tcx.explicit_item_super_predicates(def_id).skip_binder(); - record_defaulted_array!(self.tables.explicit_item_super_predicates[def_id] <- bounds); + fn encode_explicit_item_self_bounds(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_self_bounds({:?})", def_id); + let bounds = self.tcx.explicit_item_self_bounds(def_id).skip_binder(); + record_defaulted_array!(self.tables.explicit_item_self_bounds[def_id] <- bounds); } #[instrument(level = "debug", skip(self))] @@ -1685,7 +1685,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { AssocItemContainer::Trait => { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4f9cdc9a474b..d1d356c5220a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -386,7 +386,7 @@ define_tables! { // corresponding DefPathHash. def_path_hashes: Table, explicit_item_bounds: Table, Span)>>, - explicit_item_super_predicates: Table, Span)>>, + explicit_item_self_bounds: Table, Span)>>, inferred_outlives_of: Table, Span)>>, explicit_super_predicates_of: Table, Span)>>, explicit_implied_predicates_of: Table, Span)>>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e27a98236390..d83bc19a6a2f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -393,7 +393,7 @@ rustc_queries! { /// like closure signature deduction. /// /// [explicit item bounds]: Self::explicit_item_bounds - query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -427,11 +427,11 @@ rustc_queries! { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } - query item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } - query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index da4a86814820..0c22c056dab5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -349,14 +349,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_super_predicates(def_id).map_bound(IntoIterator::into_iter) + self.item_self_bounds(def_id).map_bound(IntoIterator::into_iter) } fn item_non_self_bounds( self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_non_self_assumptions(def_id).map_bound(IntoIterator::into_iter) + self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) } fn predicates_of( @@ -2591,7 +2591,7 @@ impl<'tcx> TyCtxt<'tcx> { let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, None); - self.explicit_item_super_predicates(def_id).skip_binder().iter().any(|&(predicate, _)| { + self.explicit_item_self_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else { return false; }; diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs index 21c605f8296d..cbc02097d827 100644 --- a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -64,7 +64,7 @@ impl<'tcx> TyCtxt<'tcx> { args: ty::GenericArgsRef<'tcx>, ) -> &'tcx ty::List> { let mut bounds: Vec<_> = self - .item_super_predicates(def_id) + .item_self_bounds(def_id) .iter_instantiated(self, args) .filter_map(|clause| { clause diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index bcb6ac13b8fa..5f3f65e46963 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -196,7 +196,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(predicate, _)| { predicate diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 1dd09fe7aafa..e8d14b89d698 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -293,7 +293,7 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx - .explicit_item_super_predicates(alias.def_id) + .explicit_item_self_bounds(alias.def_id) .skip_binder() .iter() .any(|(pred, _span)| match pred.kind().skip_binder() { @@ -422,7 +422,7 @@ impl Trait for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); + let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { 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 471105773e2b..f9cda83a5754 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1087,28 +1087,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), )) } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self - .tcx - .item_super_predicates(def_id) - .instantiate(self.tcx, args) - .iter() - .find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + self.tcx.item_self_bounds(def_id).instantiate(self.tcx, args).iter().find_map( + |pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && self .tcx .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }), + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.expect_type()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }, + ) + } ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0cc0d7f786b0..6b6e0b32385c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1620,9 +1620,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // projections, we will never be able to equate, e.g. `::A` // with `<::A as Tr>::A`. let relevant_bounds = if in_parent_alias_type { - self.tcx().item_non_self_assumptions(alias_ty.def_id) + self.tcx().item_non_self_bounds(alias_ty.def_id) } else { - self.tcx().item_super_predicates(alias_ty.def_id) + self.tcx().item_self_bounds(alias_ty.def_id) }; for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index bb2dc9995df7..3ccfa51ab70b 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) { - let preds = cx.tcx.explicit_item_super_predicates(def_id); + let preds = cx.tcx.explicit_item_self_bounds(def_id); let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { p.as_trait_clause() .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 32e7c2bbf7cb..607d76201028 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -96,7 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).iter_identity_copied() { + for (predicate, _span) in cx.tcx.explicit_item_self_bounds(def_id).iter_identity_copied() { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. @@ -322,7 +322,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { }, ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { - for (predicate, _) in cx.tcx.explicit_item_super_predicates(def_id).skip_binder() { + for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; @@ -712,7 +712,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option sig_from_bounds( cx, ty, - cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args), + cx.tcx.item_self_bounds(def_id).iter_instantiated(cx.tcx, args), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), From 8e0909d98a8e467f79d80000fe57eee6417ac02e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 25 Jan 2025 16:20:23 +0000 Subject: [PATCH 223/342] Move param env bound deep normalization to OutlivesEnvironment building --- .../rustc_infer/src/infer/outlives/env.rs | 38 ++++++++----------- .../src/infer/outlives/obligations.rs | 22 +---------- compiler/rustc_trait_selection/src/regions.rs | 22 ++++++++++- 3 files changed, 37 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index f8ab0b53a005..e924c974a02c 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -31,26 +31,14 @@ use crate::traits::query::OutlivesBound; pub struct OutlivesEnvironment<'tcx> { pub param_env: ty::ParamEnv<'tcx>, free_region_map: FreeRegionMap<'tcx>, - - // Contains the implied region bounds in scope for our current body. - // - // Example: - // - // ``` - // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) { - // bar(x, y, |y: &'b T| { .. } // body B1) - // } // body B0 - // ``` - // - // Here, when checking the body B0, the list would be `[T: 'a]`, because we - // infer that `T` must outlive `'a` from the implied bounds on the - // fn declaration. - // - // For the body B1 however, the list would be `[T: 'a, T: 'b]`, because we - // also can see that -- within the closure body! -- `T` must - // outlive `'b`. This is not necessarily true outside the closure - // body, since the closure may never be called. + /// FIXME: Your first reaction may be that this is a bit strange. `RegionBoundPairs` + /// does not contain lifetimes, which are instead in the `FreeRegionMap`, and other + /// known type outlives are stored in the `known_type_outlives` set. So why do we + /// have these at all? It turns out that removing these and using `known_type_outlives` + /// everywhere is just enough of a perf regression to matter. This can/should be + /// optimized in the future, though. region_bound_pairs: RegionBoundPairs<'tcx>, + known_type_outlives: Vec>, } /// "Region-bound pairs" tracks outlives relations that are known to @@ -59,9 +47,10 @@ pub struct OutlivesEnvironment<'tcx> { pub type RegionBoundPairs<'tcx> = FxIndexSet>>; impl<'tcx> OutlivesEnvironment<'tcx> { - /// Create a new `OutlivesEnvironment` with extra outlives bounds. - pub fn with_bounds( + /// Create a new `OutlivesEnvironment` from normalized outlives bounds. + pub fn from_normalized_bounds( param_env: ty::ParamEnv<'tcx>, + known_type_outlives: Vec>, extra_bounds: impl IntoIterator>, ) -> Self { let mut region_relation = TransitiveRelationBuilder::default(); @@ -96,18 +85,21 @@ impl<'tcx> OutlivesEnvironment<'tcx> { OutlivesEnvironment { param_env, + known_type_outlives, free_region_map: FreeRegionMap { relation: region_relation.freeze() }, region_bound_pairs, } } - /// Borrows current value of the `free_region_map`. pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { &self.free_region_map } - /// Borrows current `region_bound_pairs`. pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { &self.region_bound_pairs } + + pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] { + &self.known_type_outlives + } } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 0383c81f2af0..84e51b18dc5b 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -67,7 +67,6 @@ use rustc_middle::ty::{ self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, }; -use rustc_span::DUMMY_SP; use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -142,25 +141,6 @@ impl<'tcx> InferCtxt<'tcx> { ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); - let normalized_caller_bounds: Vec<_> = outlives_env - .param_env - .caller_bounds() - .iter() - .filter_map(|clause| { - let outlives = clause.as_type_outlives_clause()?; - Some( - deeply_normalize_ty( - outlives, - SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP), - ) - // FIXME(-Znext-solver): How do we accurately report an error span here :( - .map_err(|NoSolution| { - (outlives, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)) - }), - ) - }) - .try_collect()?; - // Must loop since the process of normalizing may itself register region obligations. for iteration in 0.. { let my_region_obligations = self.take_registered_region_obligations(); @@ -194,7 +174,7 @@ impl<'tcx> InferCtxt<'tcx> { self.tcx, outlives_env.region_bound_pairs(), None, - &normalized_caller_bounds, + outlives_env.known_type_outlives(), ); let category = origin.to_constraint_category(); outlives.type_must_outlive(origin, sup_type, sub_region, category); diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index fc9fa44b4c61..551717546187 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -33,12 +33,32 @@ impl<'tcx> OutlivesEnvironment<'tcx> { assumed_wf_tys: impl IntoIterator>, implied_bounds_compat: bool, ) -> Self { + let mut bounds = vec![]; + + for bound in param_env.caller_bounds() { + if let Some(mut type_outlives) = bound.as_type_outlives_clause() { + if infcx.next_trait_solver() { + match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( + infcx.at(&ObligationCause::dummy(), param_env), + type_outlives, + ) { + Ok(new) => type_outlives = new, + Err(_) => { + infcx.dcx().delayed_bug(format!("could not normalize `{bound}`")); + } + } + } + bounds.push(type_outlives); + } + } + // FIXME: This needs to be modified so that we normalize the known type // outlives obligations then elaborate them into their region/type components. // Otherwise, ` as Mirror>::Assoc: 'b` will not imply `'a: 'b` even // if we can normalize `'a`. - OutlivesEnvironment::with_bounds( + OutlivesEnvironment::from_normalized_bounds( param_env, + bounds, infcx.implied_bounds_tys_with_compat( body_id, param_env, From c22a27130dde7d7ccf5d76fd021476ddbfdecfa0 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 27 Jan 2025 16:30:02 -0800 Subject: [PATCH 224/342] Refactor FnKind variant to hold &Fn --- compiler/rustc_ast/src/mut_visit.rs | 36 ++++++++----------- compiler/rustc_ast/src/visit.rs | 25 +++++++------ compiler/rustc_ast_lowering/src/expr.rs | 4 +-- .../rustc_ast_passes/src/ast_validation.rs | 22 ++++++------ .../rustc_ast_pretty/src/pprust/state/item.rs | 28 +++++---------- compiler/rustc_lint/src/builtin.rs | 8 +++-- compiler/rustc_resolve/src/def_collector.rs | 9 +++-- compiler/rustc_resolve/src/late.rs | 6 ++-- .../rustc_resolve/src/late/diagnostics.rs | 10 +++--- .../src/multiple_bound_locations.rs | 4 +-- src/tools/rustfmt/src/items.rs | 17 ++++----- src/tools/rustfmt/src/visitor.rs | 15 ++++++-- 12 files changed, 93 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3459d39131ac..7caf7c4c3568 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -954,8 +954,14 @@ fn walk_coroutine_kind(vis: &mut T, coroutine_kind: &mut Coroutin fn walk_fn(vis: &mut T, kind: FnKind<'_>) { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span }, _visibility, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness, generics, body, sig: FnSig { header, decl, span } }, + ) => { // Identifier and visibility are visited as a part of the item. + visit_defaultness(vis, defaultness); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); @@ -1205,13 +1211,8 @@ impl WalkItemKind for ItemKind { ItemKind::Const(item) => { visit_const_item(item, vis); } - ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(vis, defaultness); - vis.visit_fn( - FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body), - span, - id, - ); + ItemKind::Fn(func) => { + vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id); } ItemKind::Mod(safety, mod_kind) => { visit_safety(vis, safety); @@ -1329,10 +1330,9 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::Const(item) => { visit_const_item(item, visitor); } - AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + AssocItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Assoc(ctxt), ident, visibility, &mut *func), span, id, ); @@ -1476,10 +1476,9 @@ impl WalkItemKind for ForeignItemKind { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + ForeignItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Foreign, ident, visibility, &mut *func), span, id, ); @@ -1965,14 +1964,7 @@ impl DummyAstNode for crate::ast_traits::AstNo #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn( - FnCtxt, - &'a mut Ident, - &'a mut FnSig, - &'a mut Visibility, - &'a mut Generics, - &'a mut Option>, - ), + Fn(FnCtxt, &'a mut Ident, &'a mut Visibility, &'a mut Fn), /// E.g., `|x, y| body`. Closure( diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1d6d7330757d..232fd546de9a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -65,7 +65,7 @@ impl BoundKind { #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option>), + Fn(FnCtxt, &'a Ident, &'a Visibility, &'a Fn), /// E.g., `|x, y| body`. Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), @@ -74,7 +74,7 @@ pub enum FnKind<'a> { impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { - FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), + FnKind::Fn(_, _, _, Fn { sig, .. }) => Some(&sig.header), FnKind::Closure(..) => None, } } @@ -88,7 +88,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { - FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, + FnKind::Fn(_, _, _, Fn { sig, .. }) => &sig.decl, FnKind::Closure(_, _, decl, _) => decl, } } @@ -374,8 +374,8 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body); + ItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { @@ -715,8 +715,8 @@ impl WalkItemKind for ForeignItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body); + ForeignItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ForeignItemKind::TyAlias(box TyAlias { @@ -858,7 +858,12 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>( pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body }, + ) => { // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); @@ -892,8 +897,8 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body); + AssocItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } AssocItemKind::Type(box TyAlias { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f31e2c65c792..b932915dc298 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -2125,7 +2125,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_mut(span, e, args)) } - fn expr_call_lang_item_fn_mut( + pub(super) fn expr_call_lang_item_fn_mut( &mut self, span: Span, lang_item: hir::LangItem, @@ -2135,7 +2135,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr_call_mut(span, path, args) } - fn expr_call_lang_item_fn( + pub(super) fn expr_call_lang_item_fn( &mut self, span: Span, lang_item: hir::LangItem, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 236ca7ba7035..ea1f4a6559ac 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => { + ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => { self.check_defaultness(item.span, *defaultness); let is_intrinsic = @@ -947,7 +947,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = FnKind::Fn(FnCtxt::Free, &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Free, &item.ident, &item.vis, &*func); self.visit_fn(kind, item.span, item.id); walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. @@ -1348,19 +1348,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } if let FnKind::Fn( - _, - _, - FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, _, _, _, + Fn { + sig: FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, + .. + }, ) = fk { self.maybe_lint_missing_abi(*extern_span, id); } // Functions without bodies cannot have patterns. - if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk { + if let FnKind::Fn(ctxt, _, _, Fn { body: None, sig, .. }) = fk { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { @@ -1394,7 +1395,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .is_some(); let disallowed = (!tilde_const_allowed).then(|| match fk { - FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, + FnKind::Fn(_, ident, _, _) => TildeConstReason::Function { ident: ident.span }, FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); @@ -1470,15 +1471,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some(); match &item.kind { - AssocItemKind::Fn(box Fn { sig, generics, body, .. }) + AssocItemKind::Fn(func) if parent_is_const || ctxt == AssocCtxt::Trait - || matches!(sig.header.constness, Const::Yes(_)) => + || matches!(func.sig.header.constness, Const::Yes(_)) => { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = - FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, &item.vis, &*func); walk_list!(self, visit_attribute, &item.attrs); self.visit_fn(kind, item.span, item.id); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 897c275d850c..4cfcaa95233d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -34,8 +34,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::ForeignItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { self.print_item_const( @@ -199,16 +199,8 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full( - sig, - item.ident, - generics, - &item.vis, - *defaultness, - body.as_deref(), - &item.attrs, - ); + ast::ItemKind::Fn(func) => { + self.print_fn_full(item.ident, &item.vis, &item.attrs, &*func); } ast::ItemKind::Mod(safety, mod_kind) => { self.head(Self::to_string(|s| { @@ -542,8 +534,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::AssocItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { self.print_item_const( @@ -653,19 +645,17 @@ impl<'a> State<'a> { fn print_fn_full( &mut self, - sig: &ast::FnSig, name: Ident, - generics: &ast::Generics, vis: &ast::Visibility, - defaultness: ast::Defaultness, - body: Option<&ast::Block>, attrs: &[ast::Attribute], + func: &ast::Fn, ) { + let ast::Fn { defaultness, generics, sig, body } = func; if body.is_some() { self.head(""); } self.print_visibility(vis); - self.print_defaultness(defaultness); + self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(name), generics); if let Some(body) = body { self.nbsp(); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c03de687a338..e8a4e9a84c46 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -330,10 +330,12 @@ impl EarlyLintPass for UnsafeCode { if let FnKind::Fn( ctxt, _, - ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, _, - _, - body, + ast::Fn { + sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, + body, + .. + }, ) = fk { let decorator = match ctxt { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 87024c487df7..16c0a345f879 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -170,9 +170,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { match fn_kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) - if let Some(coroutine_kind) = header.coroutine_kind => - { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. }, + ) if let Some(coroutine_kind) = header.coroutine_kind => { self.visit_fn_header(header); self.visit_generics(generics); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 68d3351f174c..4842cbd556c3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -986,8 +986,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r match fn_kind { // Bail if the function is foreign, and thus cannot validly have // a body, or if there's no body for some other reason. - FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _) - | FnKind::Fn(_, _, sig, _, generics, None) => { + FnKind::Fn(FnCtxt::Foreign, _, _, Fn { sig, generics, .. }) + | FnKind::Fn(_, _, _, Fn { sig, generics, body: None, .. }) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); self.with_lifetime_rib( @@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // Create a label rib for the function. this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { - FnKind::Fn(_, _, sig, _, generics, body) => { + FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => { this.visit_generics(generics); let declaration = &sig.decl; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2db8087fd832..57679d595da3 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -224,7 +224,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let suggestion = if self.current_trait_ref.is_none() && let Some((fn_kind, _)) = self.diag_metadata.current_function && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() - && let FnKind::Fn(_, _, sig, ..) = fn_kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = fn_kind && let Some(items) = self.diag_metadata.current_impl_items && let Some(item) = items.iter().find(|i| { i.ident.name == item_str.name @@ -560,7 +560,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Applicability::MaybeIncorrect, ); if !self.self_value_is_available(path[0].ident.span) { - if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) = + if let Some((FnKind::Fn(_, _, _, ast::Fn { sig, .. }), fn_span)) = &self.diag_metadata.current_function { let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) { @@ -3249,7 +3249,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { let pre = if lt.kind == MissingLifetimeKind::Ampersand && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && !sig.decl.inputs.is_empty() && let sugg = sig .decl @@ -3290,7 +3290,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else if (lt.kind == MissingLifetimeKind::Ampersand || lt.kind == MissingLifetimeKind::Underscore) && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output && !sig.decl.inputs.is_empty() && let arg_refs = sig @@ -3350,7 +3350,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ty) = &sig.decl.output { let mut lt_finder = diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 882ab2dda7aa..0e1980a6acb6 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -1,5 +1,5 @@ use rustc_ast::visit::FnKind; -use rustc_ast::{NodeId, WherePredicateKind}; +use rustc_ast::{Fn, NodeId, WherePredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); impl EarlyLintPass for MultipleBoundLocations { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { - if let FnKind::Fn(_, _, _, _, generics, _) = kind + if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind && !generics.params.is_empty() && !generics.where_clause.predicates.is_empty() { diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index e7d0fba048b4..457d0afe3b5b 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -333,19 +333,19 @@ impl<'a> FnSig<'a> { defaultness: ast::Defaultness, ) -> FnSig<'a> { match *fn_kind { - visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, fn_sig, vis, generics, _) => { - let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); + visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, vis, ast::Fn { sig, generics, .. }) => { + let mut fn_sig = FnSig::from_method_sig(sig, generics, vis); fn_sig.defaultness = defaultness; fn_sig } - visit::FnKind::Fn(_, _, fn_sig, vis, generics, _) => FnSig { + visit::FnKind::Fn(_, _, vis, ast::Fn { sig, generics, .. }) => FnSig { decl, generics, - ext: fn_sig.header.ext, - constness: fn_sig.header.constness, - coroutine_kind: Cow::Borrowed(&fn_sig.header.coroutine_kind), + ext: sig.header.ext, + constness: sig.header.constness, + coroutine_kind: Cow::Borrowed(&sig.header.coroutine_kind), defaultness, - safety: fn_sig.header.safety, + safety: sig.header.safety, visibility: vis, }, _ => unreachable!(), @@ -3453,6 +3453,7 @@ impl Rewrite for ast::ForeignItem { ref sig, ref generics, ref body, + .. } = **fn_kind; if body.is_some() { let mut visitor = FmtVisitor::from_context(context); @@ -3461,7 +3462,7 @@ impl Rewrite for ast::ForeignItem { let inner_attrs = inner_attributes(&self.attrs); let fn_ctxt = visit::FnCtxt::Foreign; visitor.visit_fn( - visit::FnKind::Fn(fn_ctxt, &self.ident, sig, &self.vis, generics, body), + visit::FnKind::Fn(fn_ctxt, &self.ident, &self.vis, fn_kind), &sig.decl, self.span, defaultness, diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 805e13b78036..bdcb619153d8 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -386,7 +386,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let indent = self.block_indent; let block; let rewrite = match fk { - visit::FnKind::Fn(_, ident, _, _, _, Some(ref b)) => { + visit::FnKind::Fn( + _, + ident, + _, + ast::Fn { + body: Some(ref b), .. + }, + ) => { block = b; self.rewrite_fn_before_block( indent, @@ -539,6 +546,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ref sig, ref generics, ref body, + .. } = **fn_kind; if body.is_some() { let inner_attrs = inner_attributes(&item.attrs); @@ -547,7 +555,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { _ => visit::FnCtxt::Foreign, }; self.visit_fn( - visit::FnKind::Fn(fn_ctxt, &item.ident, sig, &item.vis, generics, body), + visit::FnKind::Fn(fn_ctxt, &item.ident, &item.vis, fn_kind), &sig.decl, item.span, defaultness, @@ -640,12 +648,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ref sig, ref generics, ref body, + .. } = **fn_kind; if body.is_some() { let inner_attrs = inner_attributes(&ai.attrs); let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt); self.visit_fn( - visit::FnKind::Fn(fn_ctxt, &ai.ident, sig, &ai.vis, generics, body), + visit::FnKind::Fn(fn_ctxt, &ai.ident, &ai.vis, fn_kind), &sig.decl, ai.span, defaultness, From 130b0d294a3404b5827869de5712009f91724700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 28 Jan 2025 19:35:31 +0000 Subject: [PATCH 225/342] Tweak `&mut self` suggestion span ``` error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference --> $DIR/issue-38147-1.rs:17:9 | LL | self.s.push('x'); | ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable | help: consider changing this to be a mutable reference | LL | fn f(&mut self) { | +++ ``` Note the suggestion to add `mut` instead of replacing the entire `&self` with `&mut self`. --- .../src/diagnostics/mutability_errors.rs | 25 ++++++++----------- tests/ui/borrowck/issue-93093.rs | 2 +- tests/ui/borrowck/issue-93093.stderr | 2 +- .../trait-impl-argument-difference-ice.stderr | 4 +-- tests/ui/did_you_mean/issue-38147-1.stderr | 2 +- tests/ui/did_you_mean/issue-39544.stderr | 6 ++--- tests/ui/mut/mutable-class-fields-2.stderr | 2 +- tests/ui/suggestions/suggest-ref-mut.rs | 2 +- tests/ui/suggestions/suggest-ref-mut.stderr | 2 +- 9 files changed, 21 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a6ca038282d9..e841a5e4c948 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1140,10 +1140,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let amp_mut_sugg = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { - let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span); - let additional = - local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span))); - Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional }) + let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); + let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span)); + Some(AmpMutSugg { has_sugg: true, span, suggestion, additional }) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1202,10 +1201,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt_ty_info: None, .. })) => { - let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span); + let (span, sugg) = + suggest_ampmut_self(self.infcx.tcx, decl_span); Some(AmpMutSugg { has_sugg: true, - span: decl_span, + span, suggestion: sugg, additional: None, }) @@ -1461,17 +1461,12 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option(tcx: TyCtxt<'tcx>, span: Span) -> String { +fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) { match tcx.sess.source_map().span_to_snippet(span) { - Ok(snippet) => { - let lt_pos = snippet.find('\''); - if let Some(lt_pos) = lt_pos { - format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4]) - } else { - "&mut self".to_string() - } + Ok(snippet) if snippet.ends_with("self") => { + (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string()) } - _ => "&mut self".to_string(), + _ => (span, "&mut self".to_string()), } } diff --git a/tests/ui/borrowck/issue-93093.rs b/tests/ui/borrowck/issue-93093.rs index e85b296c983f..1521b2072389 100644 --- a/tests/ui/borrowck/issue-93093.rs +++ b/tests/ui/borrowck/issue-93093.rs @@ -4,7 +4,7 @@ struct S { } impl S { async fn bar(&self) { //~ HELP consider changing this to be a mutable reference - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594] } } diff --git a/tests/ui/borrowck/issue-93093.stderr b/tests/ui/borrowck/issue-93093.stderr index b6a2768b61da..d788ce331973 100644 --- a/tests/ui/borrowck/issue-93093.stderr +++ b/tests/ui/borrowck/issue-93093.stderr @@ -7,7 +7,7 @@ LL | self.foo += 1; help: consider changing this to be a mutable reference | LL | async fn bar(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr index 5c70eccfbd35..190ddeaa8f28 100644 --- a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr @@ -41,7 +41,7 @@ LL | let a16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference --> $DIR/trait-impl-argument-difference-ice.rs:18:19 @@ -52,7 +52,7 @@ LL | let b16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error: aborting due to 5 previous errors; 1 warning emitted diff --git a/tests/ui/did_you_mean/issue-38147-1.stderr b/tests/ui/did_you_mean/issue-38147-1.stderr index a0392113ab15..6def86e4ba8f 100644 --- a/tests/ui/did_you_mean/issue-38147-1.stderr +++ b/tests/ui/did_you_mean/issue-38147-1.stderr @@ -7,7 +7,7 @@ LL | self.s.push('x'); help: consider changing this to be a mutable reference | LL | fn f(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-39544.stderr b/tests/ui/did_you_mean/issue-39544.stderr index 8ccb4cbb0c16..62dc027e31f2 100644 --- a/tests/ui/did_you_mean/issue-39544.stderr +++ b/tests/ui/did_you_mean/issue-39544.stderr @@ -18,7 +18,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo<'z>(&'z mut self) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:20:17 @@ -29,7 +29,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo1(&mut self, other: &Z) { - | ~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:21:17 @@ -51,7 +51,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo2<'a>(&'a mut self, other: &Z) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:26:17 diff --git a/tests/ui/mut/mutable-class-fields-2.stderr b/tests/ui/mut/mutable-class-fields-2.stderr index eb0c54f885ba..7a6ff4da2bf5 100644 --- a/tests/ui/mut/mutable-class-fields-2.stderr +++ b/tests/ui/mut/mutable-class-fields-2.stderr @@ -7,7 +7,7 @@ LL | self.how_hungry -= 5; help: consider changing this to be a mutable reference | LL | pub fn eat(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index b40439b8e372..9f5df9303c33 100644 --- a/tests/ui/suggestions/suggest-ref-mut.rs +++ b/tests/ui/suggestions/suggest-ref-mut.rs @@ -3,7 +3,7 @@ struct X(usize); impl X { fn zap(&self) { //~^ HELP - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.0 = 32; //~^ ERROR } diff --git a/tests/ui/suggestions/suggest-ref-mut.stderr b/tests/ui/suggestions/suggest-ref-mut.stderr index cc00022ab8e3..935a04c052ac 100644 --- a/tests/ui/suggestions/suggest-ref-mut.stderr +++ b/tests/ui/suggestions/suggest-ref-mut.stderr @@ -7,7 +7,7 @@ LL | self.0 = 32; help: consider changing this to be a mutable reference | LL | fn zap(&mut self) { - | ~~~~~~~~~ + | +++ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference --> $DIR/suggest-ref-mut.rs:15:5 From 5dfe0f8cf49110491ec0391c4b94b56834d65aef Mon Sep 17 00:00:00 2001 From: Mohammad Omidvar Date: Tue, 28 Jan 2025 19:45:20 +0000 Subject: [PATCH 226/342] Make crate AST mutation accessible for driver callback --- compiler/rustc_driver_impl/src/lib.rs | 6 +++--- src/doc/rustc-dev-guide/examples/rustc-driver-example.rs | 2 +- .../examples/rustc-driver-interacting-with-the-ast.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 20be21446098..c9d38a0f9325 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -160,7 +160,7 @@ pub trait Callbacks { fn after_crate_root_parsing( &mut self, _compiler: &interface::Compiler, - _queries: &ast::Crate, + _krate: &mut ast::Crate, ) -> Compilation { Compilation::Continue } @@ -311,7 +311,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // Parse the crate root source code (doesn't parse submodules yet) // Everything else is parsed during macro expansion. - let krate = passes::parse(sess); + let mut krate = passes::parse(sess); // If pretty printing is requested: Figure out the representation, print it and exit if let Some(pp_mode) = sess.opts.pretty { @@ -328,7 +328,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) return early_exit(); } - if callbacks.after_crate_root_parsing(compiler, &krate) == Compilation::Stop { + if callbacks.after_crate_root_parsing(compiler, &mut krate) == Compilation::Stop { return early_exit(); } diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index 8983915d78a9..b0f9af1b8d10 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index c894b60444ac..8766a8173446 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); From 6763561161dff2419ffe9795ec8bc40671c16e9e Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Tue, 28 Jan 2025 21:42:51 +0100 Subject: [PATCH 227/342] btree/node.rs: remove incorrect comment from pop_internal_level docs --- library/alloc/src/collections/btree/node.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 6815ac1c1930..2525f5856cd2 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -600,9 +600,6 @@ impl NodeRef { /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// - /// Requires exclusive access to the `NodeRef` object but not to the root node; - /// it will not invalidate other handles or references to the root node. - /// /// Panics if there is no internal level, i.e., if the root node is a leaf. pub(super) fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); From 810e4c1bc62a4801a4c29fe6c975630acbd78370 Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Wed, 29 Jan 2025 08:35:29 +0100 Subject: [PATCH 228/342] btree/node.rs: pop_internal_level: does not invalidate other handles --- library/alloc/src/collections/btree/node.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 2525f5856cd2..37f784a322ca 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -600,6 +600,9 @@ impl NodeRef { /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// + /// Does not invalidate any handles or references pointing into the subtree + /// rooted at the first child of `self`. + /// /// Panics if there is no internal level, i.e., if the root node is a leaf. pub(super) fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); From 15652544786b6787af29cae9bc0bb16d49d48fb4 Mon Sep 17 00:00:00 2001 From: uellenberg Date: Mon, 27 Jan 2025 16:21:08 -0800 Subject: [PATCH 229/342] Fix off-by-one error causing driftsort to crash Fixes #136103. Based on the analysis by @jonathan-gruber-jg and @orlp. --- library/core/src/slice/sort/stable/drift.rs | 4 ++-- library/core/src/slice/sort/stable/mod.rs | 24 ++++++++++++++----- .../core/src/slice/sort/stable/quicksort.rs | 2 ++ .../driftsort-off-by-one-issue-136103.rs | 10 ++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs index 644e75a4581e..cf1df1e91a50 100644 --- a/library/core/src/slice/sort/stable/drift.rs +++ b/library/core/src/slice/sort/stable/drift.rs @@ -10,8 +10,8 @@ use crate::{cmp, intrinsics}; /// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true, /// it will only do small-sorts and physical merges, ensuring O(N * log(N)) -/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, -/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. +/// worst-case complexity. `scratch.len()` must be at least +/// `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort. /// Fully ascending and descending inputs will be sorted with exactly N - 1 /// comparisons. /// diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 7adcc83b818d..3ff2e71fd05b 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -41,6 +41,8 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less cfg_if! { if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + // Unlike driftsort, mergesort only requires len / 2, + // not len - len / 2. let alloc_len = len / 2; cfg_if! { @@ -91,16 +93,26 @@ fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], i // By allocating n elements of memory we can ensure the entire input can // be sorted using stable quicksort, which allows better performance on // random and low-cardinality distributions. However, we still want to - // reduce our memory usage to n / 2 for large inputs. We do this by scaling - // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for - // small inputs and n / 2 for large inputs, without a sudden drop off. We - // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the + // reduce our memory usage to n - n / 2 for large inputs. We do this by scaling + // our allocation as max(n - n / 2, min(n, 8MB)), ensuring we scale like n for + // small inputs and n - n / 2 for large inputs, without a sudden drop off. We + // also need to ensure our alloc >= SMALL_SORT_GENERAL_SCRATCH_LEN, as the // small-sort always needs this much memory. + // + // driftsort will produce unsorted runs of up to min_good_run_len, which + // is at most len - len / 2. + // Unsorted runs need to be processed by quicksort, which requires as much + // scratch space as the run length, therefore the scratch space must be at + // least len - len / 2. + // If min_good_run_len is ever modified, this code must be updated to allocate + // the correct scratch size for it. const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::(); let len = v.len(); - let alloc_len = - cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN); + let alloc_len = cmp::max( + cmp::max(len - len / 2, cmp::min(len, max_full_alloc)), + SMALL_SORT_GENERAL_SCRATCH_LEN, + ); // For small inputs 4KiB of stack storage suffices, which allows us to avoid // calling the (de-)allocator. Benchmarks showed this was quite beneficial. diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 0c8308bfce00..630c6ff90770 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -7,6 +7,8 @@ use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; use crate::{intrinsics, ptr}; /// Sorts `v` recursively using quicksort. +/// `scratch.len()` must be at least `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` +/// otherwise the implementation may abort. /// /// `limit` when initialized with `c*log(v.len())` for some c ensures we do not /// overflow the stack or go quadratic. diff --git a/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs b/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs new file mode 100644 index 000000000000..42197ff102d7 --- /dev/null +++ b/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs @@ -0,0 +1,10 @@ +//@ run-pass +// Ensures that driftsort doesn't crash under specific slice +// length and memory size. +// Based on the example given in https://github.com/rust-lang/rust/issues/136103. +fn main() { + let n = 127; + let mut objs: Vec<_> = + (0..n).map(|i| [(i % 2) as u8; 125001]).collect(); + objs.sort(); +} From dd616d6a16288a0e8d79405650db2e68762a4367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:01:49 +0800 Subject: [PATCH 230/342] run-make-support: add `sysroot` helper Convenience helper for `rustc --print=sysroot`. --- src/tools/run-make-support/src/external_deps/rustc.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 8894ea7fb209..b70db7130f67 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -1,5 +1,6 @@ use std::ffi::{OsStr, OsString}; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::str::FromStr as _; use crate::command::Command; use crate::env::env_var; @@ -390,3 +391,10 @@ impl Rustc { self } } + +/// Query the sysroot path corresponding `rustc --print=sysroot`. +#[track_caller] +pub fn sysroot() -> PathBuf { + let path = rustc().print("sysroot").run().stdout_utf8(); + PathBuf::from_str(path.trim()).unwrap() +} From 5f513a6a888b4d71d7949a7d6ff2688c86febd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:03:21 +0800 Subject: [PATCH 231/342] run-make-support: improve docs for `assert_exit_code` --- src/tools/run-make-support/src/command.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index b4dc753ab534..70a72bd1abeb 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -388,9 +388,13 @@ impl CompletedProcess { self } + /// Check the **exit status** of the process. On Unix, this is *not* the **wait status**. + /// + /// See [`std::process::ExitStatus::code`]. This is not to be confused with + /// [`std::process::ExitCode`]. #[track_caller] pub fn assert_exit_code(&self, code: i32) -> &Self { - assert!(self.output.status.code() == Some(code)); + assert_eq!(self.output.status.code(), Some(code)); self } } From 6f5a1035f8b3694e519598ddb27d884a591e0d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:03:18 +0800 Subject: [PATCH 232/342] tests: port `translation` to rmake.rs Co-authored-by: Oneirical --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/translation/Makefile | 78 ------- tests/run-make/translation/rmake.rs | 194 ++++++++++++++++++ 3 files changed, 194 insertions(+), 79 deletions(-) delete mode 100644 tests/run-make/translation/Makefile create mode 100644 tests/run-make/translation/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 6f0fd09b353a..e75d3dc2147b 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,3 +1,2 @@ run-make/split-debuginfo/Makefile run-make/symbol-mangling-hashed/Makefile -run-make/translation/Makefile diff --git a/tests/run-make/translation/Makefile b/tests/run-make/translation/Makefile deleted file mode 100644 index 07e0547cfa09..000000000000 --- a/tests/run-make/translation/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -include ../tools.mk - -# This test uses `ln -s` rather than copying to save testing time, but its -# usage doesn't work on Windows. -# ignore-windows - -SYSROOT:=$(shell $(RUSTC) --print sysroot) -FAKEROOT=$(TMPDIR)/fakeroot -RUSTC_LOG:=rustc_error_messages -export RUSTC_TRANSLATION_NO_DEBUG_ASSERT:=1 - -all: normal custom missing broken sysroot sysroot-invalid sysroot-missing - -# Check that the test works normally, using the built-in fallback bundle. -normal: test.rs - $(RUSTC) $< 2>&1 | $(CGREP) "struct literal body without path" - -# Check that a primary bundle can be loaded and will be preferentially used -# where possible. -custom: test.rs working.ftl - $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/working.ftl 2>&1 | $(CGREP) "this is a test message" - -# Check that a primary bundle with a broken message (e.g. a interpolated -# variable is missing) will use the fallback bundle. -missing: test.rs missing.ftl - $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/missing.ftl 2>&1 | $(CGREP) "struct literal body without path" - -# Check that a primary bundle without the desired message will use the fallback -# bundle. -broken: test.rs broken.ftl - $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/broken.ftl 2>&1 | $(CGREP) "struct literal body without path" - -# Check that a locale can be loaded from the sysroot given a language -# identifier by making a local copy of the sysroot and adding the custom locale -# to it. -sysroot: test.rs working.ftl - rm -rf $(FAKEROOT) - mkdir $(FAKEROOT) - ln -s $(SYSROOT)/* $(FAKEROOT) - rm -f $(FAKEROOT)/lib - mkdir $(FAKEROOT)/lib - ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib - rm -f $(FAKEROOT)/lib/rustlib - mkdir $(FAKEROOT)/lib/rustlib - ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib - rm -f $(FAKEROOT)/lib/rustlib/src - mkdir $(FAKEROOT)/lib/rustlib/src - ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src - # When download-rustc is enabled, `$(SYSROOT)` will have a share directory. Delete the link to it. - rm -f $(FAKEROOT)/share - mkdir -p $(FAKEROOT)/share/locale/zh-CN/ - ln -s $(CURDIR)/working.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl - $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | $(CGREP) "this is a test message" - -# Check that the compiler errors out when the sysroot requested cannot be -# found. This test might start failing if there actually exists a Klingon -# translation of rustc's error messages. -sysroot-missing: - $(RUSTC) $< -Ztranslate-lang=tlh 2>&1 | $(CGREP) "missing locale directory" - -# Check that the compiler errors out when the directory for the locale in the -# sysroot is actually a file. -sysroot-invalid: test.rs working.ftl - rm -rf $(FAKEROOT) - mkdir $(FAKEROOT) - ln -s $(SYSROOT)/* $(FAKEROOT) - rm -f $(FAKEROOT)/lib - mkdir $(FAKEROOT)/lib - ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib - rm -f $(FAKEROOT)/lib/rustlib - mkdir $(FAKEROOT)/lib/rustlib - ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib - rm -f $(FAKEROOT)/lib/rustlib/src - mkdir $(FAKEROOT)/lib/rustlib/src - ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src - mkdir -p $(FAKEROOT)/share/locale - touch $(FAKEROOT)/share/locale/zh-CN - $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | $(CGREP) "`\$sysroot/share/locales/\$locale` is not a directory" diff --git a/tests/run-make/translation/rmake.rs b/tests/run-make/translation/rmake.rs new file mode 100644 index 000000000000..86078888c2e1 --- /dev/null +++ b/tests/run-make/translation/rmake.rs @@ -0,0 +1,194 @@ +//! Smoke test for the rustc diagnostics translation infrastructure. +//! +//! # References +//! +//! - Current tracking issue: . +//! - Old tracking issue: +//! - Initial translation infra implementation: . + +// This test uses symbolic links to stub out a fake sysroot to save testing time. +//@ needs-symlink +//@ needs-subprocess + +#![deny(warnings)] + +use std::path::{Path, PathBuf}; + +use run_make_support::rustc::sysroot; +use run_make_support::{cwd, rfs, run_in_tmpdir, rustc}; + +fn main() { + builtin_fallback_bundle(); + additional_primary_bundle(); + missing_slug_prefers_fallback_bundle(); + broken_primary_bundle_prefers_fallback_bundle(); + locale_sysroot(); + missing_sysroot(); + file_sysroot(); +} + +/// Check that the test works normally, using the built-in fallback bundle. +fn builtin_fallback_bundle() { + rustc().input("test.rs").run_fail().assert_stderr_contains("struct literal body without path"); +} + +/// Check that a primary bundle can be loaded and will be preferentially used where possible. +fn additional_primary_bundle() { + rustc() + .input("test.rs") + .arg("-Ztranslate-additional-ftl=working.ftl") + .run_fail() + .assert_stderr_contains("this is a test message"); +} + +/// Check that a primary bundle without the desired message will use the fallback bundle. +fn missing_slug_prefers_fallback_bundle() { + rustc() + .input("test.rs") + .arg("-Ztranslate-additional-ftl=missing.ftl") + .run_fail() + .assert_stderr_contains("struct literal body without path"); +} + +/// Check that a primary bundle with a broken message (e.g. an interpolated variable is not +/// provided) will use the fallback bundle. +fn broken_primary_bundle_prefers_fallback_bundle() { + // FIXME(#135817): as of the rmake.rs port, the compiler actually ICEs on the additional + // `broken.ftl`, even though the original intention seems to be that it should gracefully + // failover to the fallback bundle. On `aarch64-apple-darwin`, somehow it *doesn't* ICE. + + rustc() + .env("RUSTC_ICE", "0") // disable ICE dump file, not needed + .input("test.rs") + .arg("-Ztranslate-additional-ftl=broken.ftl") + .run_fail(); +} + +#[track_caller] +fn shallow_symlink_dir_entries(src_dir: &Path, dst_dir: &Path) { + for entry in rfs::read_dir(src_dir) { + let entry = entry.unwrap(); + let src_entry_path = entry.path(); + let src_filename = src_entry_path.file_name().unwrap(); + let meta = rfs::symlink_metadata(&src_entry_path); + if meta.is_symlink() || meta.is_file() { + rfs::symlink_file(&src_entry_path, dst_dir.join(src_filename)); + } else if meta.is_dir() { + rfs::symlink_dir(&src_entry_path, dst_dir.join(src_filename)); + } else { + unreachable!() + } + } +} + +#[track_caller] +fn shallow_symlink_dir_entries_materialize_single_dir( + src_dir: &Path, + dst_dir: &Path, + dir_filename: &str, +) { + shallow_symlink_dir_entries(src_dir, dst_dir); + + let dst_symlink_meta = rfs::symlink_metadata(dst_dir.join(dir_filename)); + + if dst_symlink_meta.is_file() || dst_symlink_meta.is_dir() { + unreachable!(); + } + + #[cfg(windows)] + { + use std::os::windows::fs::FileTypeExt as _; + if dst_symlink_meta.file_type().is_symlink_file() { + rfs::remove_file(dst_dir.join(dir_filename)); + } else if dst_symlink_meta.file_type().is_symlink_dir() { + rfs::remove_dir(dst_dir.join(dir_filename)); + } else { + unreachable!(); + } + } + #[cfg(not(windows))] + { + rfs::remove_file(dst_dir.join(dir_filename)); + } + + rfs::create_dir_all(dst_dir.join(dir_filename)); +} + +#[track_caller] +fn setup_fakeroot_parents() -> PathBuf { + let sysroot = sysroot(); + let fakeroot = cwd().join("fakeroot"); + rfs::create_dir_all(&fakeroot); + shallow_symlink_dir_entries_materialize_single_dir(&sysroot, &fakeroot, "lib"); + shallow_symlink_dir_entries_materialize_single_dir( + &sysroot.join("lib"), + &fakeroot.join("lib"), + "rustlib", + ); + shallow_symlink_dir_entries_materialize_single_dir( + &sysroot.join("lib").join("rustlib"), + &fakeroot.join("lib").join("rustlib"), + "src", + ); + shallow_symlink_dir_entries( + &sysroot.join("lib").join("rustlib").join("src"), + &fakeroot.join("lib").join("rustlib").join("src"), + ); + fakeroot +} + +/// Check that a locale can be loaded from the sysroot given a language identifier by making a local +/// copy of the sysroot and adding the custom locale to it. +fn locale_sysroot() { + run_in_tmpdir(|| { + let fakeroot = setup_fakeroot_parents(); + + // When download-rustc is enabled, real sysroot will have a share directory. Delete the link + // to it. + let _ = std::fs::remove_file(fakeroot.join("share")); + + let fake_locale_path = fakeroot.join("share").join("locale").join("zh-CN"); + rfs::create_dir_all(&fake_locale_path); + rfs::symlink_file( + cwd().join("working.ftl"), + fake_locale_path.join("basic-translation.ftl"), + ); + + rustc() + .env("RUSTC_ICE", "0") + .input("test.rs") + .sysroot(&fakeroot) + .arg("-Ztranslate-lang=zh-CN") + .run_fail() + .assert_stderr_contains("this is a test message"); + }); +} + +/// Check that the compiler errors out when the sysroot requested cannot be found. This test might +/// start failing if there actually exists a Klingon translation of rustc's error messages. +fn missing_sysroot() { + run_in_tmpdir(|| { + rustc() + .input("test.rs") + .arg("-Ztranslate-lang=tlh") + .run_fail() + .assert_stderr_contains("missing locale directory"); + }); +} + +/// Check that the compiler errors out when the directory for the locale in the sysroot is actually +/// a file. +fn file_sysroot() { + run_in_tmpdir(|| { + let fakeroot = setup_fakeroot_parents(); + rfs::create_dir_all(fakeroot.join("share").join("locale")); + rfs::write(fakeroot.join("share").join("locale").join("zh-CN"), b"not a dir"); + + rustc() + .input("test.rs") + .sysroot(&fakeroot) + .arg("-Ztranslate-lang=zh-CN") + .run_fail() + .assert_stderr_contains("is not a directory"); + }); +} From 57cfcd228dd6b5873c96799c64c6186fa8edec66 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 23 Jan 2025 10:16:08 +0100 Subject: [PATCH 233/342] use impl Into --- compiler/rustc_parse_format/src/lib.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 09c88e7f83bb..d021ea107ed2 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -363,12 +363,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err, S2: Into>( - &mut self, - description: S1, - label: S2, - span: InnerSpan, - ) { + fn err(&mut self, description: impl Into, label: impl Into, span: InnerSpan) { self.errors.push(ParseError { description: description.into(), note: None, @@ -382,11 +377,11 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note, S2: Into, S3: Into>( + fn err_with_note( &mut self, - description: S1, - label: S2, - note: S3, + description: impl Into, + label: impl Into, + note: impl Into, span: InnerSpan, ) { self.errors.push(ParseError { From b7916fb4b762d406a560c5e5f6e632033edd9c14 Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Thu, 23 Jan 2025 21:25:26 -0500 Subject: [PATCH 234/342] tests: Skip const OOM tests on aarch64-unknown-linux-gnu Skip const OOM tests on AArch64 Linux through explicit annotations instead of inside opt-dist. Intended to avoid confusion in cases like #135952. Prerequisite for https://github.com/rust-lang/rust/pull/135960. --- src/tools/opt-dist/src/main.rs | 12 +----------- tests/ui/consts/large_const_alloc.rs | 2 ++ tests/ui/consts/large_const_alloc.stderr | 4 ++-- .../promoted_running_out_of_memory_issue-130687.rs | 2 ++ ...romoted_running_out_of_memory_issue-130687.stderr | 2 +- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 04de3493ea29..565721a90934 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -148,16 +148,6 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> let is_aarch64 = target_triple.starts_with("aarch64"); - let skip_tests = if is_aarch64 { - vec![ - // Those tests fail only inside of Docker on aarch64, as of December 2024 - "tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs".to_string(), - "tests/ui/consts/large_const_alloc.rs".to_string(), - ] - } else { - vec![] - }; - let checkout_dir = Utf8PathBuf::from("/checkout"); let env = EnvironmentBuilder::default() .host_tuple(target_triple) @@ -169,7 +159,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .shared_llvm(true) // FIXME: Enable bolt for aarch64 once it's fixed upstream. Broken as of December 2024. .use_bolt(!is_aarch64) - .skipped_tests(skip_tests) + .skipped_tests(vec![]) .build()?; (env, shared.build_args) diff --git a/tests/ui/consts/large_const_alloc.rs b/tests/ui/consts/large_const_alloc.rs index 61a22216ae52..14edc1bb6961 100644 --- a/tests/ui/consts/large_const_alloc.rs +++ b/tests/ui/consts/large_const_alloc.rs @@ -1,5 +1,7 @@ //@ only-64bit // on 32bit and 16bit platforms it is plausible that the maximum allocation size will succeed +// FIXME (#135952) In some cases on AArch64 Linux the diagnostic does not trigger +//@ ignore-aarch64-unknown-linux-gnu const FOO: () = { // 128 TiB, unlikely anyone has that much RAM diff --git a/tests/ui/consts/large_const_alloc.stderr b/tests/ui/consts/large_const_alloc.stderr index 25d660f1217f..fa7d5977a956 100644 --- a/tests/ui/consts/large_const_alloc.stderr +++ b/tests/ui/consts/large_const_alloc.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/large_const_alloc.rs:6:13 + --> $DIR/large_const_alloc.rs:8:13 | LL | let x = [0_u8; (1 << 47) - 1]; | ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler error[E0080]: could not evaluate static initializer - --> $DIR/large_const_alloc.rs:11:13 + --> $DIR/large_const_alloc.rs:13:13 | LL | let x = [0_u8; (1 << 47) - 1]; | ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler diff --git a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs index b923a768cbfe..53618e2e86ac 100644 --- a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs +++ b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs @@ -3,6 +3,8 @@ // Needs the max type size to be much bigger than the RAM people typically have. //@ only-64bit +// FIXME (#135952) In some cases on AArch64 Linux the diagnostic does not trigger +//@ ignore-aarch64-unknown-linux-gnu pub struct Data([u8; (1 << 47) - 1]); const _: &'static Data = &Data([0; (1 << 47) - 1]); diff --git a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr index f5d767efceb7..aac805dbd8c7 100644 --- a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr +++ b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/promoted_running_out_of_memory_issue-130687.rs:8:32 + --> $DIR/promoted_running_out_of_memory_issue-130687.rs:10:32 | LL | const _: &'static Data = &Data([0; (1 << 47) - 1]); | ^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler From d93cbe5d693b8f486b82d2b11a7cbb7e2c09c1e8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Dec 2024 15:29:19 +0100 Subject: [PATCH 235/342] Add new setting allowing to switch to sans serif fonts --- src/librustdoc/build.rs | 2 ++ src/librustdoc/html/static/css/rustdoc.css | 27 ++++++++++++++++-- .../html/static/fonts/FiraMono-Medium.woff2 | Bin 0 -> 64572 bytes .../html/static/fonts/FiraMono-Regular.woff2 | Bin 0 -> 64868 bytes src/librustdoc/html/static/js/settings.js | 11 +++++++ src/librustdoc/html/static/js/storage.js | 3 ++ src/librustdoc/html/static_files.rs | 2 ++ 7 files changed, 43 insertions(+), 2 deletions(-) create mode 100755 src/librustdoc/html/static/fonts/FiraMono-Medium.woff2 create mode 100755 src/librustdoc/html/static/fonts/FiraMono-Regular.woff2 diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 69337fb1d250..810225ca927a 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -19,6 +19,8 @@ fn main() { "static/images/favicon-32x32.png", "static/fonts/FiraSans-Regular.woff2", "static/fonts/FiraSans-Medium.woff2", + "static/fonts/FiraMono-Regular.woff2", + "static/fonts/FiraMono-Medium.woff2", "static/fonts/FiraSans-LICENSE.txt", "static/fonts/SourceSerif4-Regular.ttf.woff2", "static/fonts/SourceSerif4-Bold.ttf.woff2", diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index f487d66edac7..bf665bc61d6e 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -38,6 +38,13 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ --code-block-border-radius: 6px; --impl-items-indent: 0.3em; --docblock-indent: 24px; + --font-family: "Source Serif 4", NanumBarunGothic, serif; + --font-family-code: "Source Code Pro", monospace; +} + +:root.sans-serif { + --font-family: "Fira Sans", sans-serif; + --font-family-code: "Fira Mono", monospace; } /* See FiraSans-LICENSE.txt for the Fira Sans license. */ @@ -57,6 +64,22 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2"); font-display: swap; } +@font-face { + font-family: 'Fira Mono'; + font-style: normal; + font-weight: 400; + src: local('Fira Mono'), + url("FiraMono-Regular-87c26294.woff2") format("woff2"); + font-display: swap; +} +@font-face { + font-family: 'Fira Mono'; + font-style: normal; + font-weight: 500; + src: local('Fira Mono Medium'), + url("FiraMono-Medium-86f75c8c.woff2") format("woff2"); + font-display: swap; +} /* See SourceSerif4-LICENSE.md for the Source Serif 4 license. */ @font-face { @@ -126,7 +149,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ body { /* Line spacing at least 1.5 per Web Content Accessibility Guidelines https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */ - font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif; + font: 1rem/1.5 var(--font-family); margin: 0; position: relative; /* We use overflow-wrap: break-word for Safari, which doesn't recognize @@ -380,7 +403,7 @@ details:not(.toggle) summary { } code, pre, .code-header, .type-signature { - font-family: "Source Code Pro", monospace; + font-family: var(--font-family-code) } .docblock code, .item-table dd code { border-radius: 3px; diff --git a/src/librustdoc/html/static/fonts/FiraMono-Medium.woff2 b/src/librustdoc/html/static/fonts/FiraMono-Medium.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..610e9b2071ec1d6c47a7815010acb20f0abbdd98 GIT binary patch literal 64572 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ℜ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 literal 0 HcmV?d00001 diff --git a/src/librustdoc/html/static/fonts/FiraMono-Regular.woff2 b/src/librustdoc/html/static/fonts/FiraMono-Regular.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..9fa44b7cc2d38680bc14df07ddf3f6b320740da5 GIT binary patch literal 64868 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 literal 0 HcmV?d00001 diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 183663b94fc2..245159d8e055 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -50,6 +50,12 @@ removeClass(document.documentElement, "hide-modnav"); } break; + case "sans-serif-fonts": + if (value === true) { + addClass(document.documentElement, "sans-serif"); + } else { + removeClass(document.documentElement, "sans-serif"); + } } } @@ -232,6 +238,11 @@ "js_name": "disable-shortcuts", "default": false, }, + { + "name": "Use sans serif fonts", + "js_name": "sans-serif-fonts", + "default": false, + }, ]; // Then we build the DOM. diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d77804d045e3..1867e4cc0f3b 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -211,6 +211,9 @@ if (getSettingValue("hide-toc") === "true") { if (getSettingValue("hide-modnav") === "true") { addClass(document.documentElement, "hide-modnav"); } +if (getSettingValue("sans-serif-fonts") === "true") { + addClass(document.documentElement, "sans-serif"); +} function updateSidebarWidth() { const desktopSidebarWidth = getSettingValue("desktop-sidebar-width"); if (desktopSidebarWidth && desktopSidebarWidth !== "null") { diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 6457ac731cb7..ec59353948d5 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -100,6 +100,8 @@ static_files! { rust_favicon_png_32 => "static/images/favicon-32x32.png", fira_sans_regular => "static/fonts/FiraSans-Regular.woff2", fira_sans_medium => "static/fonts/FiraSans-Medium.woff2", + fira_mono_regular => "static/fonts/FiraMono-Regular.woff2", + fira_mono_medium => "static/fonts/FiraMono-Medium.woff2", fira_sans_license => "static/fonts/FiraSans-LICENSE.txt", source_serif_4_regular => "static/fonts/SourceSerif4-Regular.ttf.woff2", source_serif_4_bold => "static/fonts/SourceSerif4-Bold.ttf.woff2", From 65fedebfc40653d88142171252f5e8b004afbfb9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Dec 2024 15:55:05 +0100 Subject: [PATCH 236/342] Add GUI test for new "sans serif fonts" setting --- tests/rustdoc-gui/font-serif-change.goml | 31 ++++++++++++++++++++++++ tests/rustdoc-gui/settings.goml | 8 +++--- 2 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/rustdoc-gui/font-serif-change.goml diff --git a/tests/rustdoc-gui/font-serif-change.goml b/tests/rustdoc-gui/font-serif-change.goml new file mode 100644 index 000000000000..b14d5ae96f92 --- /dev/null +++ b/tests/rustdoc-gui/font-serif-change.goml @@ -0,0 +1,31 @@ +// Ensures that the font serif change is working as expected. +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" + +// By default, it should be the serif fonts. +store-value: (serif_font, '"Source Serif 4", NanumBarunGothic, serif') +store-value: (serif_code_font, '"Source Code Pro", monospace') +assert-css: ("body", {"font-family": |serif_font|}) +assert-css: ("p code", {"font-family": |serif_code_font|}) + +// We now switch to the sans serif font +click: "#settings-menu" +wait-for: "#sans-serif-fonts" +click: "#sans-serif-fonts" + +store-value: (font, '"Fira Sans", sans-serif') +store-value: (code_font, '"Fira Mono", monospace') +assert-css: ("body", {"font-family": |font|}) +assert-css: ("p code", {"font-family": |code_font|}) + +// Reloading the page to ensure it is loaded correctly. +reload: +assert-css: ("body", {"font-family": |font|}) +assert-css: ("p code", {"font-family": |code_font|}) + +// We switch back to the serif font +click: "#settings-menu" +wait-for: "#sans-serif-fonts" +click: "#sans-serif-fonts" + +assert-css: ("body", {"font-family": |serif_font|}) +assert-css: ("p code", {"font-family": |serif_code_font|}) diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index 1d93c07f9ec5..4ab5b83d7c41 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -257,15 +257,15 @@ assert-text: ("#preferred-light-theme .setting-radio-name", "Preferred light the // We now check that clicking on the toggles' text is like clicking on the checkbox. // To test it, we use the "Disable keyboard shortcuts". set-local-storage: {"rustdoc-disable-shortcuts": "false"} -click: ".setting-line:last-child .setting-check span" +click: "#disable-shortcuts" assert-local-storage: {"rustdoc-disable-shortcuts": "true"} // We now check that focusing a toggle and pressing Space is like clicking on it. assert-local-storage: {"rustdoc-disable-shortcuts": "true"} -focus: ".setting-line:last-child .setting-check input" +focus: "#disable-shortcuts" press-key: "Space" assert-local-storage: {"rustdoc-disable-shortcuts": "false"} -focus: ".setting-line:last-child .setting-check input" +focus: "#disable-shortcuts" press-key: "Space" assert-local-storage: {"rustdoc-disable-shortcuts": "true"} @@ -276,7 +276,7 @@ assert-false: "#help-button .popover" wait-for-css: ("#settings-menu .popover", {"display": "block"}) // Now turn keyboard shortcuts back on, and see if they work. -click: ".setting-line:last-child .setting-check span" +click: "#disable-shortcuts" assert-local-storage: {"rustdoc-disable-shortcuts": "false"} press-key: "Escape" press-key: "?" From 91f6e000c29db3882039569e53d091df63ba5415 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Dec 2024 16:12:08 +0100 Subject: [PATCH 237/342] Fix tidy errors --- REUSE.toml | 2 +- license-metadata.json | 2 ++ src/tools/tidy/src/bins.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/REUSE.toml b/REUSE.toml index 6b16d97ed806..9e873e94eff2 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -92,7 +92,7 @@ SPDX-FileCopyrightText = "2015 Anders Kaseorg " SPDX-License-Identifier = "MIT" [[annotations]] -path = "src/librustdoc/html/static/fonts/FiraSans**" +path = "src/librustdoc/html/static/fonts/Fira**" precedence = "override" SPDX-FileCopyrightText = ["2014, Mozilla Foundation", "2014, Telefonica S.A."] SPDX-License-Identifier = "OFL-1.1" diff --git a/license-metadata.json b/license-metadata.json index 09cc36935658..1814044d5a47 100644 --- a/license-metadata.json +++ b/license-metadata.json @@ -113,6 +113,8 @@ { "directories": [], "files": [ + "FiraMono-Medium.woff2", + "FiraMono-Regular.woff2", "FiraSans-LICENSE.txt", "FiraSans-Medium.woff2", "FiraSans-Regular.woff2" diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index d158a8e63240..9b78ba75a055 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -134,7 +134,7 @@ mod os_impl { &mut |entry| { let file = entry.path(); let extension = file.extension(); - let scripts = ["py", "sh", "ps1"]; + let scripts = ["py", "sh", "ps1", "woff2"]; if scripts.into_iter().any(|e| extension == Some(OsStr::new(e))) { return; } From 999a25ee891a20003b9004ff3bee5e2849efd375 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Dec 2024 16:12:08 +0100 Subject: [PATCH 238/342] Fix tidy errors --- license-metadata.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/license-metadata.json b/license-metadata.json index 1814044d5a47..35a362fafd35 100644 --- a/license-metadata.json +++ b/license-metadata.json @@ -117,7 +117,9 @@ "FiraMono-Regular.woff2", "FiraSans-LICENSE.txt", "FiraSans-Medium.woff2", - "FiraSans-Regular.woff2" + "FiraSans-Regular.woff2", + "FiraMono-Medium.woff2", + "FiraMono-Regular.woff2" ], "license": { "copyright": [ @@ -268,4 +270,4 @@ ], "type": "root" } -} \ No newline at end of file +} From 895564e0381e816705990ab5354739e8a64b158d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jan 2025 22:28:34 +0100 Subject: [PATCH 239/342] Add italic for newly added sans serif fonts --- license-metadata.json | 6 +++--- src/librustdoc/build.rs | 2 ++ src/librustdoc/html/static/css/rustdoc.css | 20 ++++++++++++++++-- .../html/static/fonts/FiraSans-Italic.woff2 | Bin 0 -> 136300 bytes .../static/fonts/FiraSans-MediumItalic.woff2 | Bin 0 -> 140588 bytes src/librustdoc/html/static_files.rs | 2 ++ src/librustdoc/html/templates/page.html | 2 +- 7 files changed, 26 insertions(+), 6 deletions(-) create mode 100755 src/librustdoc/html/static/fonts/FiraSans-Italic.woff2 create mode 100755 src/librustdoc/html/static/fonts/FiraSans-MediumItalic.woff2 diff --git a/license-metadata.json b/license-metadata.json index 35a362fafd35..4cf9bea2861d 100644 --- a/license-metadata.json +++ b/license-metadata.json @@ -115,11 +115,11 @@ "files": [ "FiraMono-Medium.woff2", "FiraMono-Regular.woff2", + "FiraSans-Italic.woff2", "FiraSans-LICENSE.txt", "FiraSans-Medium.woff2", - "FiraSans-Regular.woff2", - "FiraMono-Medium.woff2", - "FiraMono-Regular.woff2" + "FiraSans-MediumItalic.woff2", + "FiraSans-Regular.woff2" ], "license": { "copyright": [ diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 810225ca927a..5e25c588cd95 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -17,8 +17,10 @@ fn main() { "static/images/rust-logo.svg", "static/images/favicon.svg", "static/images/favicon-32x32.png", + "static/fonts/FiraSans-Italic.woff2", "static/fonts/FiraSans-Regular.woff2", "static/fonts/FiraSans-Medium.woff2", + "static/fonts/FiraSans-MediumItalic.woff2", "static/fonts/FiraMono-Regular.woff2", "static/fonts/FiraMono-Medium.woff2", "static/fonts/FiraSans-LICENSE.txt", diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index bf665bc61d6e..71d4ca44da6a 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -56,6 +56,14 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ url("FiraSans-Regular-0fe48ade.woff2") format("woff2"); font-display: swap; } +@font-face { + font-family: 'Fira Sans'; + font-style: italic; + font-weight: 400; + src: local('Fira Sans Italic'), + url("FiraSans-Italic-81dc35de.woff2") format("woff2"); + font-display: swap; +} @font-face { font-family: 'Fira Sans'; font-style: normal; @@ -64,6 +72,14 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2"); font-display: swap; } +@font-face { + font-family: 'Fira Sans'; + font-style: normal; + font-weight: 500; + src: local('Fira Sans Medium Italic'), + url("FiraSans-MediumItalic-ccf7e434.woff2") format("woff2"); + font-display: swap; +} @font-face { font-family: 'Fira Mono'; font-style: normal; @@ -273,7 +289,7 @@ summary.hideme, .rustdoc-breadcrumbs, /* This selector is for the items listed in the "all items" page. */ ul.all-items { - font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; + font-family: var(--font-family); } #toggle-all-docs, @@ -403,7 +419,7 @@ details:not(.toggle) summary { } code, pre, .code-header, .type-signature { - font-family: var(--font-family-code) + font-family: var(--font-family-code); } .docblock code, .item-table dd code { border-radius: 3px; diff --git a/src/librustdoc/html/static/fonts/FiraSans-Italic.woff2 b/src/librustdoc/html/static/fonts/FiraSans-Italic.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..3f63664fee6ddf221f71a57461a7f4d1de281177 GIT binary patch literal 136300 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
_2Vt$`LUedf6{zmk^mFiEgy_atH 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≫Z$OHx4eWbW1E?5o%p_ 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 S^KaC%*UN+?Bwk@=U;qHNWc059 literal 0 HcmV?d00001 diff --git a/src/librustdoc/html/static/fonts/FiraSans-MediumItalic.woff2 b/src/librustdoc/html/static/fonts/FiraSans-MediumItalic.woff2 new file mode 100755 index 0000000000000000000000000000000000000000..2d08f9f7d45fda39315519c4d6ffce5b84454d70 GIT binary patch literal 140588 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¨{$-yCTqVw{kyY8 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 literal 0 HcmV?d00001 diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index ec59353948d5..319119514820 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -98,8 +98,10 @@ static_files! { rust_logo_svg => "static/images/rust-logo.svg", rust_favicon_svg => "static/images/favicon.svg", rust_favicon_png_32 => "static/images/favicon-32x32.png", + fira_sans_italic => "static/fonts/FiraSans-Italic.woff2", fira_sans_regular => "static/fonts/FiraSans-Regular.woff2", fira_sans_medium => "static/fonts/FiraSans-Medium.woff2", + fira_sans_medium_italic => "static/fonts/FiraSans-MediumItalic.woff2", fira_mono_regular => "static/fonts/FiraMono-Regular.woff2", fira_mono_medium => "static/fonts/FiraMono-Medium.woff2", fira_sans_license => "static/fonts/FiraSans-LICENSE.txt", diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index a05d6ca83132..5ef376f4acbc 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -7,7 +7,7 @@ {# #} {{page.title}} {# #} {# #} {# #} From 22f074129a27e229b30c69b8e8d600dee3593537 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jan 2025 11:59:29 +0000 Subject: [PATCH 240/342] Add tests for transmuting pattern types --- tests/ui/type/pattern_types/transmute.rs | 34 +++++++++++++++++ tests/ui/type/pattern_types/transmute.stderr | 39 ++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/ui/type/pattern_types/transmute.rs create mode 100644 tests/ui/type/pattern_types/transmute.stderr diff --git a/tests/ui/type/pattern_types/transmute.rs b/tests/ui/type/pattern_types/transmute.rs new file mode 100644 index 000000000000..54aab6e3cb24 --- /dev/null +++ b/tests/ui/type/pattern_types/transmute.rs @@ -0,0 +1,34 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +// ok +fn create(x: u32) -> pattern_type!(u32 is S..=E) { + unsafe { std::mem::transmute(x) } + //~^ ERROR types of different sizes +} + +// ok +fn unwrap(x: pattern_type!(u32 is S..=E)) -> u32 { + unsafe { std::mem::transmute(x) } + //~^ ERROR types of different sizes +} + +// bad, only when S != u32::MIN or E != u32::MAX will this ok +fn non_base_ty_transmute( + x: Option, +) -> u32 { + unsafe { std::mem::transmute(x) } + //~^ ERROR types of different sizes +} + +// bad, only when S = u32::MIN and E = u32::MAX will this ok +fn wrapped_transmute( + x: Option, +) -> Option { + unsafe { std::mem::transmute(x) } + //~^ ERROR types of different sizes +} + +fn main() {} diff --git a/tests/ui/type/pattern_types/transmute.stderr b/tests/ui/type/pattern_types/transmute.stderr new file mode 100644 index 000000000000..f348740b1059 --- /dev/null +++ b/tests/ui/type/pattern_types/transmute.stderr @@ -0,0 +1,39 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:8:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u32` (32 bits) + = note: target type: `(u32) is S..=E` (size can vary because of u32) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:14:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `(u32) is S..=E` (size can vary because of u32) + = note: target type: `u32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:22:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) + = note: target type: `u32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:30:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) + = note: target type: `Option` (64 bits) + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0512`. From a639e276f3bfb0e0538bad78f3d68ebb7877aeaa Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jan 2025 11:08:22 +0000 Subject: [PATCH 241/342] Allow transmuting generic pattern types to and from their base --- compiler/rustc_middle/src/ty/layout.rs | 3 +++ tests/ui/type/pattern_types/transmute.rs | 2 -- tests/ui/type/pattern_types/transmute.stderr | 24 +++----------------- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 0d41a1d7dbfb..b212992bd2dd 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -504,6 +504,9 @@ impl<'tcx> SizeSkeleton<'tcx> { } } + // Pattern types are always the same size as their base. + ty::Pat(base, _) => SizeSkeleton::compute(base, tcx, typing_env), + _ => Err(err), } } diff --git a/tests/ui/type/pattern_types/transmute.rs b/tests/ui/type/pattern_types/transmute.rs index 54aab6e3cb24..cb76b2b938dc 100644 --- a/tests/ui/type/pattern_types/transmute.rs +++ b/tests/ui/type/pattern_types/transmute.rs @@ -6,13 +6,11 @@ use std::pat::pattern_type; // ok fn create(x: u32) -> pattern_type!(u32 is S..=E) { unsafe { std::mem::transmute(x) } - //~^ ERROR types of different sizes } // ok fn unwrap(x: pattern_type!(u32 is S..=E)) -> u32 { unsafe { std::mem::transmute(x) } - //~^ ERROR types of different sizes } // bad, only when S != u32::MIN or E != u32::MAX will this ok diff --git a/tests/ui/type/pattern_types/transmute.stderr b/tests/ui/type/pattern_types/transmute.stderr index f348740b1059..578549b515c1 100644 --- a/tests/ui/type/pattern_types/transmute.stderr +++ b/tests/ui/type/pattern_types/transmute.stderr @@ -1,23 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute.rs:8:14 - | -LL | unsafe { std::mem::transmute(x) } - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `u32` (32 bits) - = note: target type: `(u32) is S..=E` (size can vary because of u32) - -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute.rs:14:14 - | -LL | unsafe { std::mem::transmute(x) } - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `(u32) is S..=E` (size can vary because of u32) - = note: target type: `u32` (32 bits) - -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute.rs:22:14 + --> $DIR/transmute.rs:20:14 | LL | unsafe { std::mem::transmute(x) } | ^^^^^^^^^^^^^^^^^^^ @@ -26,7 +8,7 @@ LL | unsafe { std::mem::transmute(x) } = note: target type: `u32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute.rs:30:14 + --> $DIR/transmute.rs:28:14 | LL | unsafe { std::mem::transmute(x) } | ^^^^^^^^^^^^^^^^^^^ @@ -34,6 +16,6 @@ LL | unsafe { std::mem::transmute(x) } = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) = note: target type: `Option` (64 bits) -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0512`. From 2511faf61d82dd32fa40f1724e6ee74d51700b54 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Jan 2025 11:44:14 +0100 Subject: [PATCH 242/342] Add SemiBold for SourceSerif4 --- src/librustdoc/build.rs | 1 + src/librustdoc/html/static/COPYRIGHT.txt | 2 +- src/librustdoc/html/static/css/rustdoc.css | 12 ++++++++++-- .../fonts/SourceSerif4-Semibold.ttf.woff2 | Bin 0 -> 80732 bytes src/librustdoc/html/static_files.rs | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/librustdoc/html/static/fonts/SourceSerif4-Semibold.ttf.woff2 diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 5e25c588cd95..b4b0a8d0615d 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -25,6 +25,7 @@ fn main() { "static/fonts/FiraMono-Medium.woff2", "static/fonts/FiraSans-LICENSE.txt", "static/fonts/SourceSerif4-Regular.ttf.woff2", + "static/fonts/SourceSerif4-Semibold.ttf.woff2", "static/fonts/SourceSerif4-Bold.ttf.woff2", "static/fonts/SourceSerif4-It.ttf.woff2", "static/fonts/SourceSerif4-LICENSE.md", diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt index 1447df792f68..111340298c5e 100644 --- a/src/librustdoc/html/static/COPYRIGHT.txt +++ b/src/librustdoc/html/static/COPYRIGHT.txt @@ -36,7 +36,7 @@ included, and carry their own copyright notices and license terms: See SourceCodePro-LICENSE.txt. * Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, - SourceSerif4-It.ttf.woff2): + SourceSerif4-It.ttf.woff2, SourceSerif4-Semibold.ttf.woff2): Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 71d4ca44da6a..d0612e997fd7 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -74,7 +74,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ } @font-face { font-family: 'Fira Sans'; - font-style: normal; + font-style: italic; font-weight: 500; src: local('Fira Sans Medium Italic'), url("FiraSans-MediumItalic-ccf7e434.woff2") format("woff2"); @@ -114,6 +114,14 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2"); font-display: swap; } +@font-face { + font-family: 'Source Serif 4'; + font-style: normal; + font-weight: 500; + src: local('Source Serif 4 Semibold'), + url("SourceSerif4-Semibold-457a13ac.ttf.woff2") format("woff2"); + font-display: swap; +} @font-face { font-family: 'Source Serif 4'; font-style: normal; @@ -289,7 +297,7 @@ summary.hideme, .rustdoc-breadcrumbs, /* This selector is for the items listed in the "all items" page. */ ul.all-items { - font-family: var(--font-family); + font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; } #toggle-all-docs, diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-Semibold.ttf.woff2 b/src/librustdoc/html/static/fonts/SourceSerif4-Semibold.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..dd55f4e95ec9c29fb566d8617104afca31f0eeef GIT binary patch literal 80732 zcmXT-cQayOWME)mw2oomV_;xl(YnIGX!VbQh0&b>3y^e^YUPq{TWTirN{uan;bH<; z`y!Us&-vWbiWnHpm{ge8Ca@^5PLX4OUl$#4eDlrxN9Vuqc`-Aa$JRM8%kf~y+8sMz z-Z{VDHfU=s=PU)ij$Jst+TVCaWc#YMa>`Z4K@)dP&o?kPWeeSZ`$)W)BH3_HGm|R+?heS{9%9$1HI&o9pvC?}cM;|R(lA52}P^qdH_SNOIX|9uR z>6@;URNusPNjuLf#KgS~ICoh|k;U<3_XnjX?~gs66?K7S%~QJ#BHcfdrB7V0bFyQV zH86|hiF{+i61sPt=Vi5>ojTK;G-X2fY&AP{>s{ZgfK5|^4E;>i4c@!n@hT1#H1Iw0 zJNSO!!pq4r5l0obPhi;D+Qq(cflYhRYORLm1Bwh&i&$fw92rCdm>fJ=RpVE_mtW#3 zti&vRN%^#g>I8q=?@#n?CJ9Y>pM1=ScY2^-rqL^popVl3NaR^OXX_uAV#!Byx%xMJ zV#yA#xqEr@%ht8?)AV)qZKS7J{X3Rx}H?JGxH}M zxWF~Eio zCh_mPKN}e}_g*W}T>9u5!?)R*&LuU^s>C(cG@V|4{?!qO1?ycR<}BBFe)-s)=+{{d zX@2~ttdD!YYMHJ6$LN+MPf_Y-?K*#{_Sp(&G#8%T{9)%E?<|9j|E!l!a`^jjPX8g3 zjx59N9}M3v_nUsD(O)U-(V@L6o!S1KhmXeXVe`)LEEQ;twvJiv9-j$Y)t#kD2JheQ@AQnnx5OjjQQXgc zyB^p2>F$_y{LIpAB4wXeADn93A3G_>ecG<@bxU_{GubffU2inI(Z)r|vGpIiH*kD8 zuu0*Xfas&jJY5yni#ZWf*d;$jhGk36_B60C?K>j<^7ZMPPoFC@v;XgJHZx$^|Lnnr ztA@Te%gT27?dRJfK40OGjF?iizGHA7b56}XnOotCa=rg7C2kyiQ-8o@tC?a$U-VMu z0~UcoD<_#4ExxU&*4_JC@zJ8s0`qOo|F7L&d#;vubDR8TjSU9pmJ9wVKG2z>xpk5M z+EV`OTbz}DJnK5odO#%LZDWv!e88#5t;t!l7=&IW_~)K`XSAFr`mKyQi+}LexesqY zo2_tdO+)BOnbe@n3u3RLi|zkkUhc8pH?ME2rX!>LM@Mt9Un1w%{QZAh&F_4*wVdV> zm52@fp%WFvbS{+Le!F3{*VXUF>uWymxp%zu^7FGXPMu4W1oa|Vg-^Lfa$jnTD?DkM z)w)#u1BY|o0>w5dMQ0O>Nz$2%xbr+X^l~;`XP+W;h@0ur-8bpq6`7d2l9*it0=FEW zcFAOEX3?@MQ9UbRvtJZE_ zyL|21TdC1+cf8(kIxjc7I{fx)nFG0gJL7teX!K~T@!DX({FqhWfU70`%fZD$j=uxl z)uysi`naE8{+K zmTSd1=uLQcP3r1HpIljKU-?%{lotv=jFVt!YR}Jmzxmqpj?}Hz9>V^zt1?vG3Ku23 z`HCc^Ym-%O;Tpd46XdL4^A~W6eJ!>9^Z)f?UW=J0gtIzSZAx}dW1p6}peFhM z4e!NC3mDh}SW~6){3P19{d7`S5GYx{e$~nw^+)w@JhZy{?)J%)8L!mz*rPjlexClV z?j+xT4rh}x4HHL({|~lRNPiR5ZVmCj+%dUA?a$|bZ`s)YUz4l#S-={|Fop4P&aTGz zL)P^g-uxNU_;3GN|Eb*8_j3lvB?d(ntIUm~g?GB&5dWIY0mCxrx|I176+*KXW zzHX+rK>v@HQ0Y^5PAt8-C4-q+OeIjxukp?7>0i|E{(2D4{x&?+;_|PqvW%P4Qa3BE zS>!o^pE0Rpo$9}|XDLl*x7|(@|Gz#WV)KR#8#a7v1%sp4bwFTEWCTP!E%EH37m=HH z$eM?4*ON=<5K1;-le)Fk{9AMVKmW4~{`>6qO^MXC(@m^wI>LP|_)Avd`rm(6R;Fi` zO|$dhS|Fp=`1VKp#BQ~#iL5>A_g{~{`1FR1#5``HnM*qLx83!3V3c_#TeEUQ2d-ZMThU# z>y`KPUgErv#=2#~lufPwWY=Xn-q1SJu2<2hJvsXR*@=RAd0w;AL`0vdujY-B`&-Q3 zz2)j$t8(V2{U=*fB$Fl15-siv0*aY*4VZqoU)xrl{@*D}>p}As1sAO; zS+CVzzgzuxO5bFi_a_;b>iwJ^bl?xq509Pq+5gXce_mNH{<~pPihlivSJ|HD6xmdEHu<)%|m77Vwtlc{#4+6V>GS#3QyP z@8Cz?!)Zt9UIgHoi_*0I=}XB{JN#WsnU5H#5cTaZD!~sHfrq+6{2LAv5`d0?(KA$^#%TLw=jFAFaMOBA(|NZ|z@7wPKS2pkb zylP34s%X$EskxV?WaiG_tp8m?z~EkFVcgs4_fP96GO-E&zlZzX->$oU znR%z~%#>_ZR)MIvf=!*GVO=jSuVs1AA8_>A`{TzBd|x<8YvqbK^(&6ISFbpB?9ZKj zB5zNpPTO)UuI!Y?yN|{Hysz&0*B{8Jv3I*fA$Q912`4Xe?+m^5b@yq%)z|mjnUu^o z{pbJl44alM(q6~23| z#M6a!k(;gmc3fVv_q0>L;ggBe1h$`)-_-m`Vq((IH#SnOi>_(RahrK~F6;8X=a;)n zW%P94lYyJs2q_uHQ=EJZ)1rC-u}x;LiFH!xAf!@jKTeSYf z>zrrew%Mv!qr)MW$oSl%;@{<;H_yfXwLBgb)*+h2{wvqJ-)+-ZlX?7Wu9i1^mHqV3 zpkn%Lt22BIVJxmQ&2n!keX)u4DSwd26n;Zahb5u*Fv9|d#`g2l^#{^fCC=sU`aS)9 zyp!`OjV8T=KiIARO*U7Tj(;dR^RHIPyjU5@3rU|He$V_X5VGZCQ`P0D1i^Xw(@eeZ zuU1>NQ|z3NQeD_unGEG`}zVQGI-Snfr+b`;)#C?yW1C_0vn+hJ`Jc zPjjn-lt$LfOZIQKJf2(pfV1=cUdD+o3mC2bzSCNN>95t_`OX|V#-1;7Q#e z;yy#xumfFzC#&S*m7{;R==^a%J+1#HN6Uq;@uAn3%u`hrzfG%HQU38T+aIY?!=iy^~Ye4F=!HbvlB@tGVkB9pE?IrPS>=P{g%cZ(8YV!K6iJ_8t z=4)mhaen{%&Uz0)K}{hq&D04&Y~Rn@XnVi=b0hzM;fjq050b73%a-gfpXe*@aWFmZ z)ytSXqn?$eW}ffM>dus8Dlr{9I792ygm39D{`{|AQ&01}l^AvOZ_eo0K@PJ7Pt z>I(}Caa8zmFRAc2UPUJ9Rcn5Sl?YUCjw+QA*`4gi%0Bg7oc3+Ux$|GO>Mz_8 zclAf2qR|y;;jlYbAN2RGkoY}s8pENEW1n|y+q>)IM`8Qm4?-%QjaIeKjCb2h2kqdT zsN%mh+3L&go!|a&?pvKG?;E)(QTyNWC+b#{e->-0&auk<)iZN*+VRZnLS}_~8S}!w z{af_>zMHFf?rdk@#&vQJ9v!&$^IX}k$+7EbmCpSr8=f`0=7!OgBq#CHPu8zGZ@DGo!)=XR6Z1Cd)4hj}B@1upyrGg~WL7!* zlJDDR7oHti#>*=uQoElw&)IE(0kH3`d%i{@|^}l(QB~Ghsnt4t}Y*WwG=hkL(>~Cs?Wc;g{6T9ff zc|pG-zId+_kBrQBoK~Lqxy`>u{P=u{!br<`ci(@C>DsUOSBpztbZ_YUauxTy2a7F# zC0kv4T(JLaZjD-ol3HGnTK1=3c}IgSzZPc{H1BUc+oyOnGk&Vuk+f?u7B3gFo|+l{ zeB0)BQ|GI5oxpG-rV#H&!H#hD?J*%CtS){R{=I5H;%es@#zE-+^+DmHf zuAI>1oT4@7;l193VBWIpU2*t><0-LU%w1|H&I`D?vbRoDKFdX;amJ)QVXmtlmHf#r znwVbI^>Eg&6NXPuR)+@P3VG#zKC>$3G#V_vb6qg=+>IPQR;Mbf8*( z=kfVWdL~~I8mBfW80bs$H6D8KheL&{_uwOz4=t}%C!J^#(VC{_l5Vi@kdELs{!>kf zi;`rVadg+y>*8fGNvCCF<&tmZ6J^Ldf_Tl!2lg00A&3k3_Oj`eMU6W!q zXU^{XN$UHaX(=DB4=kM9vM*PB?~SeRUak8rSMIz1#@EJp`Tw6DhwtCN^8dc;hu+V3 zv5lFjEcN%^>)LVw4%O{Gd!rtFh;vyLa865ZAxr1902SL)`*bVB`;Kf3YDs$Yv#{m; zKgmCQyd~;kLC<&Pdx;$2Fi+I$b-sm#PV;P6$l1Xk>mL}0Bw|&SA-}6msQL2d8 zQbnzc4$gP>nOcejomi;iurU7lx%dq^M_+yQXZh9U?zH&v{PkRsk@50{W9yFOJ3&&WnbW3STogKc zO_`;vRq4xXi^J#FF|+YXnk8Kc?-0x93TsGr@_BRH{Bz0^5vCqZ#%UsaO79={Utzjw zklwve;3NMhr_L>fssA4#kQ-dYNX1T^oFaYo-{c zJAYKK{;1p$F^S#sXJ-L_-HCv^lNWB_WuJI6%y8xn&pB5XIUfx4@$KQ6IZff$WaYZ2 z2?eWe^_(bD6#cMR#@jvadHuvqN8~mq`+I!br9XFNmGG>~|NM?>)V(wR_vHP*#}zh# zr&j8S23wj2nfG3@$-QKFchbar-5=Lm8f!f^R^2&sW@Y3Y(VO#3yZ6 z-qT**(hB{(C!dY?_7~}Wv+v#f{;K+VuI&foM>qe~D81*dEw4}CKYxF<{f+)l+5c>R zSl7?JCz}6Kw(k9Y|NHCp+c`TdlqPB@o$3@!>a*~WQ9mc}yi>SCy;I*a(e>t{&nYW* z?EVq>$?;Pu*R&(*Pd#31tk!7PT(2qbdEa}ImtSCcFn>^gaQ>wiOFwTZ-Ewxz>n*!W zf@iMQ3Ah$;E#g|#w#oPW;vMgwahNC8{X zXC9wfedhR??@t3ow!e9L_v!4@^QJrNyT?9@I~SdkqIim+d3#TYhepg~uTQOwtJ+qvT77CUI(yc%$4rS+@#w-zi9UgKyeIQK zb;GVM$$GS*Dqu^Ig@^Jdrp0HsJid@H&C{ZA$3DHAb%_gC?a2DLVUksvb^E9t0FS+94LpQ|FC$_W&w9W1N z@+WXo;idJbr8XGP(VEz|rN`)jn#`mKUe4T*<1<@yL!6$i+^|54Ggc<>GT)i37c*jv z81mjW#c!O$nA!dIvR;$>g+-yWzrBhGZLnqO3Dj0nQu?pBJ>mSZPpbM)LWO%=78;)Y zRan6^aY@+RDqbnm%xjB6XO}^8)%M>ve(_3}q}=ETOg`rO=ZwW<@h6Fvr(Ttx-MdCQ zJ$cUA`G;nlvRG%-ygPnsrbOsuv0ST?mlx9y6nJd=xn$kV)l1KPZF76H`D9LR^M>*x zaeLeOxeh+a6Oais>EiUC(Bf6DwtDB&8b;5sD^YRhr51Z?=ic3y+`U=*)w$|5;k8xu zt6r_JzWX43)9bkJJ-gp$EPSVb=I_aKA4}i9-SbRe@^k!^qhC5=c?v$uxmZRRyjo^k zzveOwZ%UsbvjE!PZnTumfQ%o0E zt$SSXs9>etyw&9&52pWdZEAPmUc2SuI;(fRn=i4S6-`z2vF~X=+VkyLW&D*pn`UW; zTq=_A{d3G^3fBdJFu#DPN6uC+4AVAM?E5~cZOi$H=96b4^EVZm=5IXBxLc+;s9tMQ z<-FY6KOGcAI#u=7<-L=e7c?`bQL}f)$()V1ICdXCefzk4{rdl5-=_GeEMKrB$l2G~ z+x`536GzT4C9s}4by8bH%k-j5r&IDQUkjT?kD%3ld)aztnPgvF<-412?yi#87jM_b zUdum_z+dQ^)ES}{UUGBtva>VZ?tC|EeXC~e0hgtBIXWFowv>EPa_tmTo|b-0U%N7N z|2vKR2fFV+)wdjSa8M8kGTyrSlJz9<&)Qr+j+Ou1_$m1BtX&}?v*x*Y+qL8)&+Ki}SW-l0O?^78rvBLbtNWVP{8?bh75}EGM#21j!Z2XC3;zDuaC(DzWOvf)4O^hxtE## zw`DDxZNELF_{5GGo*t}L7v5iCKA>dNt|J>!usCH!lv;@3PJZ7Oqc*Q+9?{0VKhJKn zJuD#TU6FjEYm4Q9ie-`c2X1Xxw^HukD&D=9cU(9%qxVPD?_VD}Ci*+xi2WXVI=7|i zVNF8+mxbMXe>00os%G*poN2hU%V6QcyG1j5*u-b9sc@99bzK-xs-XNM_RyX0!c1mM zcym`Kxy#3{eBB`t`8hu&cjfAh33kV#_i{h%wcq?%{G{zw{*``CvwUrM^52-I->kU! zb4`52sSmGqPkt!i@L`i*Y@s2K0#l>#T%{cWcf`Xl`8*LlxV$k;YsL2~^VthLW|+-y zi#a(*nCG_j4mXxbs^^$quU!@Q^t+sX*Le<>1CG=4Hcgz%JDWj&^N(NKb){Y~8NC)! z+5V$}-%2UHYWY^@6^kZrjhR!{e2&HI%B!wt(%;>RqZvvql3Z7Fbn0?0nXzKxgSTnn z*Fw3y|2G|cGd1av+g#?e#qDJZ4}FVHneX~~@z2fATfRRkIdxJ}v_#vt-=L(+q{kY-ZqK7q`GnA8U zO!%boGn$UMuwQz#AUOP;lki2;`TYX74<(epXZ?HiX`gsz>?Z?jzFn1?kL}eR>&#x{ zME)w+D&q9m97nV+1w^Xv*YUQ;V_wOf0vMv&PS8@J(#^Qe$el+nt zW8bLpamIJ&Sq6`LT6(yzig&wZPYZHuU9~J~by<^;?b@#gQm`^g$*Bc}H%9oO!c{ywy}j_pjs+4JZ3OR>4VedBZU z`kj`(RSjw!$K;Oq2P}E+C%JC%E!`7YXG2w8**7qr+~}mrvF!Wq7fDOjzL7LzIdaOf z;(*oCM=kyS4`PyE8aT9E*;5i&(i)m=%wZuXQnJb*aofhvo82}Grls4dKR(=W`0!#Q zmX}Zp`sDk#cYL6-{AHf4J~|A^$OjuFM#>V$~_7_WW@i z3*T>V4|=h-ZrjQe*U$6+x%=??jR^-WW<;xhUC%9?cJ`dx>!d5?d+yh*eKI?z-a9^i z^}X{G{^|S-{OkDl%IEOo73aF|^|Sw-vAD$Z{EZC$`_F&e#bhN%&av>)2LRa*Ya{kR@u5adW$%7w@rR^Eo|{zHydHI13?@+ zVsw~|EJY^rguE$>;PL0q&~gb+IQ`9a^(0%Z*dTX`P)6%a`U!w`#d& zbkDv!(Yo35Z>vCMlI$ z&uq4daNNJ(g!^p%pU2&9Y|33;wtR9y!K5VzqHejXJ5Q6To~X*E-vVP~aR z_1P^K#aawQUWpq|ObC3Ox<4#nv>Wdmn)xi>bS&<(eAhDB&+={0=5v8Sp3XYKW0nG(_1$d-mILr&)j0N{?SPB5A|tl zFTUbwTkN~(*+=ES6Dm|fC5_ZnMRl9qB=54>HZGrbJt->I`}~O;N3JyT_~guSGWp`; zz^5l?rxV+<)A9Bcn}iVve%c`VDO!7u z$bN}WQa7Z%IkrLKZKM~o*e&|bJ};kCINzKEK($FmE+ ztbf@!FDD^HT~uW23I@OGR}L3@CmxVIr6ItR%yhECc*TrNW_`0;Zkz5WX7%kjIlHDj zW5;VwUH`oc9xr8@6do7YSyuUR+1~%>tAA)qNVs}Dc;Q-c#gHT1^JN>=^K9XL!V&dhYkG^mm@{3m%FiK zi|4&Pw$wJIbl#bNu0Qn>t}~u`wV!?vflp@V@`)9zPE@zz(&=#O7VFk!+jQj9s)icv$tyN#tyZ)~4r@#dn%vyHB6B5rkl{_%*@U*MtVN7sg9+ZN1Sn~@Xk zb}{eymeYAM-yB#wGt@P6-%OozB_W%mboc$hJGCLZ*ss=VG=1Rz{xrh5^xMLn@~ha- zwm+M1$kg0F?7w3xa+p^&7)uoHS8odplxLz@Ce&!y*WqBuS_Q(d`)t3LLF!%p$(EeAOE9$k|7vnF+N>R&}a znWxn&_&uECx9;2;OOcezda4iOZgEU+pMUhnD^BHBcekxKd#3NL{M+`Q|6%=)?Jw@9 z6|n!;dtBY}J-y1(a$7+C=}TYaPigRP(6`U!a$e*hB%`1xn6#^Wd6#D%>o)V@=IUE~&*CON=zNzJ_Xs%)Po`VK{HE;g#UkORahhFUzMUK7aIJ zRmIb|51jj4JgzXXXelUqPq^F@Wp`G%_vMxCH*~_M^_=12_KpnJyRE}--9CNtq#cur zg5~^;csbm-BZXCUf&yN7-dgcsS)|=rBbV-J8CSz1)hA_U`ib0;_x`7=|0%Gbzots` zj{eaGr?w?BH%+h3F#Wooc}mjc9~B|D7?{o6p;T%eG1*PWbM>bn#)O~lrn*~Yw!I2@Y&6qs>!p_` zG|qj=^7xwe($IXq-pA;-4)*JR37(j&F;R2UYp1m?%MYwLpmKaoZ|KBWYdy=j*p^9M z0TV(dO`Cjq*@^|LRxImon-ivOGpFv-(iO|+E?hKmPv*17G0zNRPi?*VHT>t9od-`n zQ7@k6R%`HM*)(74-0e;;Sv!xNu1oiy@3Hf%`q9lh-rs0z-uZ6L^mF3UzqZY{dD?Y- zdd-=KeGS{$I#YQ554{#X;`@2}K13orTCx<%34?ybCe(7X9U;d`Q` z^Cw-K(Q;hr6N_V$b$PqXEg6}w`}Q^8Nt@+yR^oTJ#Z4FOMT~Y0v)@hs(dC_QRnr3a@ zc4O%JKL=m z?|-PDB;UKghxjHbcYb-bkZ}v^vR&IA{Fb>Ld+W)3ne&&Q6moxKWxlZ3Bu&BhgmuM} zr{R3O-jiOd9^b3v>Av_y9oPLUSGT8LlaO4gWB8!FX5r?$HuEzCc1^y1hS6j4Ki;g% zGkOeoQ^hXYtf~Dd$F#?DSKbS!iQ;{BvL`bO#JzXeJ=l6E{~HrqigL%!iZy$EW;)%R zk+f;1j@qf@&t*Gvcz?`d(p}isJ8$hi*(-8l>vcYbDDW-nn&pwU4{qN^_`Nj*!uJxULdXtfT;^N6*82nt&vU|>4#^-hnSC`ML`Rc2F!196G zl+>^$m4mw<@bsTv!{XUgeo*p^NO3_)QOTl=F19&)_u9G!n>f6@C+A+|`^owHJrCvH zFOE`~OA33GWiyvd^!laHYjnvX$g^2M&z4!)_I_lEn5+54n^%2H#P4#3+|ii2>FN?) zPk$SMCYFpl5LKdh$6uP6kM6ZUm{Is9d)V!jCgRyouQgb5@r@r@) zVN_DdP!dyncH+{Vq*XoMQw3KV$=}t#yj*|%C#`}l+P!&CxfiFXcsISM+x7nM%adIu zC9?TfFL`SmN%xQpTG=*5@$!T$ujriob%p*S2D9D$Z7xg#{?-4>ZSC%ww2-LdKP zu6?lkOE%-FmSu5V*C)+>mvi)m(A+A;sVZ|TQzEq7=hg(dM)n##?+QG8h5f4v@6=hk ziCG!#bu->=`L*E5%EVX4R8EGhjr3k6b<5g1`zXumy!L|Gzt}l!?g?)>a=qN`l;V3X z>mWyQ&8K>=vX?7MP13i{PdOzKH-odvwQlv>89h!q%X@du{WOuU_r2cLUKyjW9B;3F zN~%@A-e8t+>_Xppzke+v%M7)xull{x-1eva-lBu^+h%Jiel+BKax3w@K+czrX~`bd zcN|342Pf_e)H=~z@T~Rr%Yr|r@9!&Ie!6qff`aV{zRNkTPHvqX5cI2P@yQud(r5Kr z^3N%sHkCI`{1biWkI7@t+r?{7+0M&8yl20SCsX|^*_BcE_=Eh9&H2a2`Tuq6?#}Z) z;vzCNr#u;_f6LGhROxDS;%GK{Y+HV_Y5MDoTT_DOO?NB)()#vWSjjSj-`{t1PIdk& zG*e@3#?#wnj{n>C{!;xoaoP7B!AI`SOr2BG>siM2Q*QD4sPz6+m0}+Ag~ca3`(vln z-`HU!H>1VxRCrpH`LXWuwI>&^NSw;5UTD&P(ZL@UapZQOVNooGwZ6fs&+(jde8-`vuqG&D5c)c1AJmq{nC z2fdudv$m?0YxzB)RS!3)Gjj1SYK^MV__>4mxb2d8myb*qnrP);cS>`t4Yi9AN=Q4&U|Tj`8DXproX3OT?{%JwBr+dNYzq- zsq&o~bv$;O7K)*2O%ewzzv)kLTN3UQR=Z94Y5S^UPq*!!?IoX(_2pIXC#ReH{Q3jl z{Y&|3lvuX6r6F*CwA*_@jn)0PVk`7F&O9{7dS!r0FVmKUn>9UaKbjwtQ}aoDyx7m6 zTmIC?m=d0*1^x5q{?Kn<`l9V^+&{j9&ilWL9hIw%<37$lpY8JIu9ItgkAIOkdOLHG z=7!tZ+Vh%LE_zhxIU(zSx10#S%KW=_61(pnoMCikdsAoIlGAs;KAu_bWRj^rYtr(h ziy;SPrhkks^{dnUDl9I4q7;#3fTC_If;gy4l zM_)B^zUmWnvR1KQxWBYN@Ym`l#@Z8VVVf?;%lw#cZ0!@d+&M$!OiU_3!ZG!i71KKx!llx)x0L=U>z>S z{ExOt7q(A%s_|rg@{{+*UbA|9PsqPvYWpG5^`XTnR`!H)2S{GE;*LLI?Xn=*+nr&) zmBy@?<(KdDq|RWt;v#fUVe`^8jvs92rzl%Bd=X5uK9bM8`E>Lx;}D~R?y;qHwTrDD z90-;+kNmw~vaekuue0 zKMD^jl;cHZ6IdSK<9xMu<13zqb>?pvIn6J>@Y|V?r1$7@^I7AVI_B8EcU%?W!n>xf zt8Lb+oqQ*6##Ik#4Qr>Pkw-4vPIy~f$8hp|?o_WM-DO(s^sbg$r!V5(gT%5*U~zniLrrB@Q_7tY8p3 z%+S^#!EnDNQ>ji(Hl_5>+PRCmR*Psc8~*&fqK0KffwI`YR99yG4x3*F58K(4S#6J8 zY~*b0SQE7_UwhsAX-b~6mP|jjHfM9|VeOp(hjvFSQdqS`Wu_35XON1fXSTNFBvsEz zE=r!0miRTWeV#c(%4g26nN_|%HBaRiC7#;*_tXdOuZ_#Iel*q0uXrE)=B_vYoHV%` z3v>3Ee0WyE-glv|=>0CGR|f>R7=0a2x4E)UD^^&z=731Rtl|U#Mi-Waj`MY%t4VhU z{h4DG?EK23?5p*yDMl%Ak($f5|JY&E#x#v*`S6`e&6&w>Kb}~dd^lSb%sqE zhSJ8*=bZZ;v#I{V6Wiv?c`{A@m*%~2*l6lepC{;ME3lz?&Wz}Sc@fY4G>OPPj*e%a z{`0tI6w7t*yN7h98XD#u{g|1S{zzr9c=ZR}$7$S5bBg6EX8w35@AWmsaruFcb5U3R z1-voc{$jgCl)$aqK6$TnUoLUYz8_d)BxiO(W@@-|vc!@Tk4#q2RB2s#T=LZFoEa~- zTED8_a9+RjeNs}u4AHb-CM%pJi(kp zR~eEoQs?(sR7vFWf}bsB2iJD-cz@yl8059zW?QCCp=-lz>#0Xi#V_It?|;*FezWFwhJK{6RbgK8!84?F|pJ@x7 zu#cU#Xo|L4Ebn9eHQqKAi!Xc$eSDPPwaa1t4KAZy2PMu~oQ~aayOnPj@3H3(*tcEo zeeGNPeEpGM-+tIEc%;(RQnz7oip+6&Gwn**6PBO$I8KwkVdMUk)oO3Tzkn>mg#v4| zJ%5>8>F&69D8m1hMeLkI{a)7%A6^c&^G}`iRAleIS*vEPnw6HB6?`@1>XlWo?RCFq zuMsv%lNOLV@?pcwAcLMmGR(aWYczkY%375*Yw1eg(BRc&vcF#DZ(3#+I=6>$p-a?} z31Ye343`#}c_mteIJ7ne&Pd+Y=%#Q@#O>gWT#NY1#0$0;FFb3Tcd;!jPNKx$WXsNl z>8`7oSFBjIY+YEaDie$F!H~9BqC$t||0^qOJT*h^RhRnVqYgaJ9C*_7Z?o9D$5YN{s?q{V6(C>k0lx_6P*szsl)`mI6&9NBUM5=BBvPAo{;HtCv#=CvgO zQI$zY3qnmxLuGlM8J$tni`~h@D*vD`w}_dok;j3}ad~-+bo2zq6W0`OTzGRx#cPsn z{|jDKA=VQYWSvXi6eh3eu}yNRH9XQfuWpLyh1lRXjYhG>dDPtFR&L`Jrm7IbXVN|O+#wOr?X%9s~?5A z<%Y^+91hEom!E1mVe9`BZb>KR{9NdEJyClmSmbTS@f#%I;-#X3S3j=>v-~AxWgd1ND><|4 z=9Zh9oV)yrT`Y_4^!%8^e}*H|c=zJS8QcmD3^p7J4Gt_!+>HkgIQBT`@N`{Y64-St z#A33H76f+vgCXi6luC`hIRY?85OlBoUgs`P7}PVj=FDBer`Tk=f4G?|D6IBBY8 zYI^V=Gg7@$^-CjQQr%UkbZn{}?Slei0SBUwt-MfCDE71z-Q*$YA zO;BI)VI#9NZ_a}=@wMW)PG{B5%$k018ta=8w>fmD^53Uw?p&P1Y-SX&R=_LP&Tb?CNnC&9xX)56@ zRs2_JO5{o1U8Qj~+)eZDZb`Fii@&yiqTIyk^1e&scdw{%ve1^DZ#^$?>-kUZo--@% z*t#_LrS9WsJl%BAd5`u2wLPoe-3w-x&9bZV>Oc55ZFT023&(;2T~;?PUD>z)3m@zJ zE~(OOXY1|xQ$E&o{fOuMH22ebzi*eVw4NBHuZ~}5**m=}K;X@n4%NL9nT-jtQ%~u2 zvZNLmNQikp*ytlL!=*&HoXg+u)<;%%pJmP+r`K1W*giYfe%0sxpV|d=8%qjxrCpkW z*FJ5idd0qmIrpraL+k2Qr%dCgdr6BG`A`E@`uA-E{uymp$IR z-!=(Oz4+md|K)}bwd!Bc?=?QVQT(5z&BTm}U->q>Z<>`< zy(MnK(ZpxVEN=#`J@2L+!1*?`A?UqxSX9o1d0FM^;#^C%zN_M9Dk)w6x5{A|&mJvH z%leYTMQ`Q(EH?%UG&jxWGhP)fIziN@F;M@~rc=5(d`N-x8+$)_$NTRmPI&qy3UR%;mwSDm-`neZ6E2 zahiW_>)Ec=KA|zm%Qr=(zdiqLbL{8aA(hwPZ_DM^Pd?ta=8Zyv?{6-pApQfcD-2)F zQgkj54dIEL|8ocT*5E+<76q1^aLw%uzL_#JH~scl8TQ(Frk3Z#TB}+9kFFk33n@>@ ze7i?Sy-CeB`-lBaMf)8CEx_~RhvK3U7MGpXhUHhQ0LR%=vVO|{}GTFm=QhUi5fUtLb(Q(#Vs&%=sSwPt z=oy1Wy8E53m@WO&maUI_k#%d+pHEJ#B9Sj%S+A)x)Co+QagyW5-Z$Bz)}Ci)Onuk+ z%lq*P_3b+&J=eNi(0rQgP_O!R_cgCIEA;*2+(WC+CW_cHy-YaJeMx>fllGNs4B3}| z9FU7$vEh?>ZupFKFHdjTXSCtpp*1sgtd}hJ)zsRT!?%w6?Y)nT{nF>Ywn=Z$-Q>Of zg4Lr5B3dscFZdT!`%MgyFm!z?@=M@rNv`MH6&cYdR$R{M%@w^EGCzUqa^=CZ0Y}c> z_`W?xblcgwZQE7P@GU+h`|8)NeOekBrVID3{lLrYxcsTw#UQg25&JdSg@iRMFFjJs zm0K^?_vz7=i^mRriEWDzYG3z7!La`b<_ zAIc5B8?cmP-m%coy@nEICsVE^oms;A@KVLXsA;Rb4dpE_TOI#A=Xgl}!!ljn{oZXs zp0i|+ZfF*{9xLBtIz_efCVxPA$;t=jmnSrJF5lO4_MFkJle2&5%?Up&P`LEw;yJ1l zk9SGT$j-d+Q{&StI}Z1)a_^i^uhN_RxnEyJ*!au!*3I?vPhS2YY_VY1-rh&&GZGrx zm;OoBU%yLUH168chyF!(Z~uE~c`CL&uV+4k@4*$vI8&Y}f0E!nIB$tob#}p=%=zp3 znx}7AAKj|<{M*wx;V1sdH=T0*d+V1*oX02Ag^mxJ8nXg+PZeO(Gt@t#dil+5?}&TN zKSFj(UK5u~(49~u+8rv@qi%RjWJbuzLgpVY;$<7BZt%)~v+S&E%Gy0J$HnA@vlUL7r6(f~!$~;w(eeyl&h=*%p=D8i16rN#Izr`!HFh~B? z8QH1Rdap#rtxT=vSedr4{pA7$W|iAZuBP4>oNK+q#rNFw6;q=ETmQ|l6p4shV>;Pq zPvH`kGD+U^3QIZ8r0Hw*7jie=S#|f0qN>~t-2#v4OP<;D98R92`j3w}zLU|^_Lt)Jy4on5b1 zdKvac>tBuMiP>`Mg5ymdURjZ6Qq%a@9z8Hfk+~fEHgAIbrm8yi=dFMBgEceuO60Ds zN_waG-#EVI)^o+LQsqywjvajFRH*Z<@6cTh-tzK!-|V~l7wi>2T=4vM0+Z@_8#Zf^ z-zzP8#Ejmt9J+tC_#G#En3iA}Pi^Tu5ev2(pEey^J)`Dh&&_q_Y2lukE>WV5b(M$v zYggqz=5lCVc2!v_R^TCHvVT4Ml7i4BPmg*Ov8(Jd3Q|#eefh}MiCg3r?Gx`_@qo)y zIak$7eeaATe#Z*>{w$reCd!%jv7f@SmcsT7-8jMBx;0-stzykNFK_Qye9$HO0!QbG znL8^~BpKh_Nl{+Nt$Vprzmbb`rm=1W@7bvvxsCJ|rpJA7`mUhKu~0hrk=~>a!S`hpdP`e= zDJ{O^c{u0x#hk=d-RieJr$5>4bn)@#U0Pz#qM7#U1f24Ida}H8pVJzh?7b(rVtZB; zhH1ae5j~>jvGkh2gaZu+e_c4c>s5MEuoBm+r^)8!TlSP}-qdr1=herL8kz-;HzICW zeVs6A;^c|K0zv{p6FEF?cwc4-6!3Amz$rKVcWWmV$X$xrp-PYM^t!g9QS#;iK{4ZMylLYkQ8(=Dsrszd^a)W((h#9 z3DqeMb>8Pryl6^Mv6Nm|y|`LI@#KQ<@~h|m+2Z9^@A^GW<>l7xIX5FM6B(J6ldF~$Z9l)4CH&4Zv0w$Eg)M7#?yOq8@?Dbuy47~shfn0bkxPH&e4g*y z)Yr9&0)ZKt2J?A14(-|(Iq}X+gDulm9llll-#~Rs_m>rIWf!-upII%ynY-@G<~_xF z%S!X-%#ohCYN_UxIh`Tf@19~l;A5KQACQ=Hgsmg%#45R?=F9vPbH24boVfXevHo!p zz8^;aYDAy^Jhbue%^6K>Pmihg=X_dluBl}ad+^^YJ-;qB$Gv?0=azPE67#v*9YL{S zQk?xhN%LMXXqF#QURay3W|za!XVYqz%-HcJmhbwF389CYx9ra>@iJ52J;~x?yqxH- zR)N#DlBTa&6;B(#vSw1t<@<40rRcBQy}QY8zlIq4rFyvBJ@{$cws1$eh|oC}sZIN@ z?q4~5`X?J+9mR($D+-ww<~DFyi)I!($x>Ib9-MC{lgJ?^3L>+GZb%TKmWt!eQnv4V*a_R-CMr& z+?}PYtsXKvTc-5e38oi~tk;Sq4_ZlB3t1>0wO!brdVKHV85IkQHggrXCVt%e!tdca z?fYl-req(qztlAIUUqo+@vC!tayDnSzn8zK5M!b9?P5r{^dYxqs}26ymR+yUaCk{M zcFaiHc=fj1w?w}yM|W!|>Gf$WwiG&8d~k=GCz|?Cz6saMxR5Y;Iu4AE?Qr^~RV{YXSFn zHto9{#v9TWopGDNJga*D%U$QoRZi?(nZGfJEJGu^LyYlPeA-rp`2byl8)K5pRTjzH5E&rEm?4R(LK%Ys%%{| ze)$9>E|1g*-nH_lPt3u%X%=he#frylJCYmvc9(|dj>Xp$xRzNSHn}OPc`)+Y$#q^l z=UguCva{WBS?|DB&qFsO0?MVdt5p&Wu1BUZt@D=t+oLu&H$QOOu9Z1%myb%7XndZN zc2Yt$nW?P;~kTaff*d{&1Zh2K)#b8g+W zhgS9Khc!IB!!j=4??@_jYt3O0-PF9SXXCpdX|bttIeVQ-S+4#HuG(Y%cdrKT9^RTo z&)>(X6k9RPG0L?nRLy!dw{xT5i?!B4YmG(K#GIL(kJbGC&2JriscoahskVPFD)ji~ z>*U|Mc*{b`J^QeSOz|eOzC(fPI%mIR^>6;5Wn>xmdE?QW#g8mzy_^+3J9pz%vFQJM zIrj)~Mm>6eKQJly-pgdicl8Uuz2S9`UB64^=-uwwZ`tg6lJq0jS06NE-l!Wp_G54V1DmWl-u z?z+8gp;kJdzBpW)Qv17n(j`5K-WLZ`78l6}@820Mf0=d8suxwy7p}Bf;uNbn+pTi1 zzu%NOvenm-J^F#ix*WRF z*Ia$a7s+-_aq7Rbd1u`D8$PC7YjN5aTr@pjaBgse<1PK;M@;X0*t(_tcdUN}gR*5X z}Q|yq7aqwpeWH5ixRQnYr@E`}yHnlg=%7bvHd`6L&b#>Ay;Tb=Jk3TtBiF zy?Nc)o&V}t^nbAcb{QoVt{UY;1sOBe7_SeS%6l#vPTbj0xoB=mN8Zn~TMqTDT&dF1 z{o%Ks$Bq}9dRFJ1*zr#F+@|SGfBIUZ zW@(dcyfjzv`h*kyw_l#!XA!(rL9DyK!c_b2e!mItTdi-M)4yN%&%*G9T3_jb>_uww zthNz5COb>`x^NdO9WQP=(K&~AQtTYb*bTwywf8Pw*n0KyrRPqi35xULdQBVj^4~nY z+4YM>#BA~DmR)Q7E%$3^Z_)Sp-FuXAkL1DrnlBkHx{eD!DJGh%QoACxby~ph_b!*# z749J zEmWOrux4Sjb8@x!7GAbS#{>Z#77!I6A#$RjL7`E5K}&;?M?)O%gL^zp785zH{_M-? zIktS)q>oFM^1jHkvE3fgtfs)gsKYpkfhFNlW%h&IT|#I7O#L9X$~`z>$5EDi^}wlW z3LFWX6Ic=+?fKJms6l1Mfu}VSCb(agxbmZ&ce{F_nzFptM%4;e`B_i*@E;MHHJ@pt zr47TB_l0j)Xn6Nteg1!aS(1u*?u*+p$Bza*bBMX=)S8^Fll^%6C!?ji#7p@_EJMVu z=~rGcnldk=)jr9Z`$h4U#~LpGS{DW?FRZx~Eww^r%B&5FUo<|nIbN1tb0=8xxluu& zj+Mrp2@iy2r<7faS}*g^TYKVtfippOue`VIjf&Mfbd@Wk?#YxG#nP`XhPKX=oQ*wh zt&@B)XHA#y&mEKh%s+7F+{(|6k*n9d+wF8}XU(sL>m(KbeU>@N&Q@8sGSa^DU5>@AOS&iTacJ>w zn#x+A9#r*md+?iGA6kO%3Leeh@YQ%%`>`X^y(=%Qm>C#;&@qc8!i(XxPVQCZO`EH& zPM8EVU%cg;J2l~c9aHpUhh36R_UeZo>My+ZWktSNeDov(fknqXTCOA%&+7ZZu$ceR z0!ywFm$xR~UwU#!U`lbxUA_7L4a#=T6jQPJzVF+yf(I+qC7(t=J}n$R!F8(a<70{| z{I|YeD7osF66a}N(SzM(Uv($H+A;U-@4~~}lV{d1xV+1{TkAyr_ro&T;nhXwJcX8= z_*eFGW1V7V*c#`r2d?z(YQAf~xls79SJgG^DN`pgF>bsc@Bh@+*V#hl+G3GjpS|bq zG4e&WZ%r+iu930WwQ*A3sjTBl9Ncr2{|R_kET45;`VV(q!IWEQc`uT;sW~o0>-Mn@6hkfPz&z_|}Z{c=%FyX?X zD8`O`_t(Gl=cGFiH8~iy$npOUa#F=>`H&-*#G%>98-ualW4`&{*L*7&V6iGz18QCbnVNA zE{%wr`uaZ}dYHKF=1!jIUp4FftGWB9>&2Yk663z;a10ykw}KT85Bgsmn)c2m=rdbg zAoHG%qj$sOHvM!fRBz%m?_xjtB-p~qC~)d=%fAY9U2cY{H($+Ip=abV_vuyEiyVOy zJW`7mPn}}e%;CTwGsA)5lFKO-Q>hsa4uUH;%beaQbDDF{t_6A=ixd)<810<2DPx&} zkrwBX8m}XX!eVP|nx-Xm{+!ZrSWPFwrl<4f#gp?7bAMo}U12BkOzNHN>!aO_rVHBQ z_9XC1vJ^9&UCnq@H`GHksP$b-h+?RRYVgSvcfKbM+fv;4w(&hlaNqXifmFNrgRINz z42q>DwI2;PhLMSj{Ttg^uwI1k4>^{I&N86 z-`@ZJ&dFQBk^P_Nznm#tYr2r5xIo`~w~z$?T6+fWz6by0zHWS}Zu+@vUvu-ly~_o6 z$~)VC5=!4FcY$?&#*!+pWu`W>w*O7~yzA=b*&K;)w&?6y`lWp4W$rTPJ|3o5lBYji z4tnda!zkUKYqoBMeQQ?xx^})_o-aPvvR}3HWc}o@HL>9>pNHXvX3rmNDWVg#izY1c zt9W6Wp|!*3!++1Jmkasty=liJRW4&Gp^i zo;=S`6%Jj(cJ(*tc%I6wS<{g@nWLH1s{ZKa%Zn`e9+P&7>mN*Bdiv`}i!WDKCZw-y@AVn;x-;ocPvtXg!BzoBP?M^4<;Ya_X~04+Q?{VzR&3 zxx05`=Gub3`HOj-mOsptP4A6b`=+4U_Q;E^OE)TX$ETIk>47XgUw_3jZg*7u_jyz40{7zTI~s-B3ne7a2Ocr)dat87sOzm(E}3ujHN`D`XP({TRjIuH67P18RR@$eOm4aSc)_8Q-5gmldpegK^mP|G zogg`-(5lI-M3zlC^&^W?r=+a6*^@mdu5q1AK4n{)- zHXf^q8!svqUre?6 z_wlrgE1v1o>P<7Q&h&9_dwE79+i3b$*Dv9v20Khl^Q4~J z_v*ju{If5%SlV^(8h5!GbO!Z#zMQrxg`$sqND+?~RaF#q3z@Mt&967~R!%6Z~ zKtq)Vx6u?=jRSM9emC04WOBIZ%>L_#9_}zO;b`yO>zN$D+jcoYfw%2K!VR8dXA-vX zv>jLQ;AuKoa6w;H@ZC~=ffuX~qctMDwVV^9G-SGUJC2GSR64LuBgK_bE3{CP%YESv zUb~-#VUN$O-8=8=4SR=g@=xa~pUxH!4L|K7LUBGQksRCi+Z$${#{Yb>0Ex;NWoh5r-i|*dqS=PKh)dqGoH@hIB62o zb^)~)E4D2Cyz#`$xx%{6r}I_Tdl*%)D=?`rx_{!;{@VEQfWgF{N6f`Hv;}ptv#;Ir zdA`=h@8W&Sc%35l7g{jQdv7?^@5Y0>LGz6`UP`aYEn)R+)B7pA$?WlEshUTNcAQfX zaay0@+qLL)!kkP&?L%R@x##%QwO`H55jLpVb7GV0jwt=v)g6jQ3R}|7wy67htm0pu zwbJ8(^`u2pdP2F9~d;p$~w%{Ob)%F z-Ru4BO|tO4Nujy3>`ts%>AEYfH8Ct+_=2aKlQ;j)UCr7kjUwE@#@0e(T$!qWb4O^D1TxwAy;83|m+wb0q z%S?Sz=Okvt96TodX@xTLt_MzpI(cl}Ikq67p1Q&|rwGDm(XCEhw#b%J=CF zm)j!F?+R&@YCM)CFzJ44{ywHS&M)be3?6qq9W5rAtmo9SU*dRJFz%VKn(M3_zwG>y zo(l`rZ#v(7dpJdSSG97?ly}QEYb{+Ev?BW9jAZW1nwQ!&`8KWYW!$zqCFS+fzn6|| z>b(=2|G{4TQ1ZUSiv@Q(1OMNyx$@~%YyM`z{&&l47x6FeIkU}uuf~ESeSz(XyG~6E z_`Bm>FjsS?TA1JYCnD=VmpadI5#QHRQlwPkvFBRN66c?>sZv{QD-Q_=ZQL;NZUx(i zW2YB&m3`N`UhBu5?f*h&xu5G|r(VZpGgjO5bo@G2ynK!31pbv$KPyBsx!e>!cP_Hv zw3A%6a_z>BPbPbAm@zR&B`4zMq35?7I=of-_iHwRLZWY{!@L*{g(2a`W-ay$uw<-zqghfMlc1= zcRcLYGs__J)E3L#Cl0h2^_lGtS#~CJQ`+fy=jLt_*N|#I{(b)W{&{-~E+$p|6Ihb@ zeW9D+w?)nqeeE&09W}b)8JJ zE&XwMwLxgA$#YLlU$4nQOZx)b7w{EIeBP4sKj(LW%(^q`1#{^qfrDXscH0vzVp-z0mYsN0#apuJn$1WyE+twI=sg$Ckzq z9j;R%oNio9Wm#wZXZ73=`y69G6-5obW9E8thnaX5T)!y%utMbcyt1p&_v4N=v`p!l z(sLk>X*VZt#Fb56S8}gx;ptJB!7@36Mft{z8KD`P^CwL9R8Tj+^la6~+qX>4$Wg->dZ{wLxAD`emCk3yc+I-L>)PuG zS5eier%ql?p8D#V_R<@2*)i*jLN)re+IXZ5Q|4}AouHt)Mb&^aNNDnfO=nU)O%gd2 z4z|Aja6>1RHQ*MDgWIm+Klc67clx>SDC*}mi%*}Vclhv;k5=N<6D?BI_0@&lf0w6; z@YfzXpd|O}N=u2y@?RfV7nFPOo_)B`-lMgIH{+uE5-0hEdl~k!MpsBk9CFrLvYGL{ zfQ4{liO2VgzBkuAILRNR-s~W@LP|w*$s|8Lj;5P$H%h5^c;`gVI56Ae)e<3}Ai*F5 zfh~Ez%atap0h!-{WRSdru-B=wJ60j zaP{0nscu};BUNM4+7>w3?{2OOZ<&!Mwg0Z(zoh7j`)>OCygodb+IVnAto~jdgBV_k zHCIlaHkzxyaBA5L`@ce$b})<1S@P|x=EgVjZmlN-ObefC|M2t+Ub1kL(aF7AUPPSk z>vC(7)AhoVY4e3;-*}iT>`RW!U-UHjR$Im&^~mq> z?7g)&>aC-hVoU#o#BWs0l6kpmmhs-psSe7GyGm!-EsKj^^i*Wy4g;rFj>e)o(DtEMVL*jw9Grbc%8CsP$-Mb#6SN5bqODfUp=Z@$m zn|YZJUR+7<`sn7e?^URCy4r+!cIUbi??qjmGbgU(;%m2Ua?h4ulV0B|-Zb@J_)g;^ zA+N7mzrFf$#jDh%Ug373Q{$ePmwnC0`x+Qb=MQ&ZS|YtNEtGryOWt4mE^(F4m}3;kFLB>MlEbg(!E}L? zw&2-$!GY@k_p&FJ_Wye@b?phkW@~pN|KO$bc38~qyE?TmR)6~!yDdNYP6^rh3p&NL z;9nI#Z_YYYow>M+vt#4ap3ccl z=4Bsm{#m54PWVe#>3`R(wQEkX>TSQeyJ}YWNjHl;>rLY4q+<=EoR~f}sDJA_$IbCq z>G$3J|D07OI`>&Vox^?Ti05JXW`&nA_WS>|6|v0uF#X$kcJH#gbN`)^R}(Xv_H>)u zOIt`tVg-KRsTz`KIiKx#mJH{)-e| zrZ!(c*HPjOQJr{ZP3vYYwOQi1W$XnE>;(*rw^(;8bToZg`7UVNWyOo!j#plqJX}>= zxNTF(rws?5Pe@G-^k+{xbj3}} -Eb<%+&&%=)2UGiGg;|CvFUz`Ec-X6eSuuUmu?UiiF?@(Y~cSZ4Un zSj6RBWP^@^M%$vdB_~qXybaB^^mBc~n%S5vu|k1)(*r)Mr@EgUZdpCos`zy1l2y9q zoYHNZznzJFqGGYL=<4Lq1z|ekJ9n`A?UTuzv-TJ7HgAqw-!@jivtY@)d7!=Av@G?Q z!Vp`TXu9s0Ta&guSjjk8=Qq_2~b?TCr^^(kNW*5&?)#l6-Xrpqs0e7CG;)y`0# z>G$uyJapbAHh=$B!L4n5k{d<#NuMyPejAiMC2jRCe(q>i1Mlzce=8zpKbK|Y{L>m- zHY2K)ccXgJaiO}micLq3ZV_xuow~#1-?f#`roY?L)_ro`*Tmndf~m8wbE#{3akE67 zRsQ9q#4t}x;Dz(W+crX4;u-%Ax_sIAzmn(9g7wy)A5E4#QTbzznpf>YJy-qxX(eq; zjG3_^dpg2i#Tg_#V#w}m)1EOwlu0Em(ro6!(ty@P0 zzl&H(2R~Eym=`L#w#4K|XW&ZxWyZ^*){19wz4$%P>}&SPES>hUDS>l#`}2v-^0N8i zcjNIa(`NZOsp&IYF4bNB7BBOwvH7e?Q2W~lc6qX2b(HEJFJLqOaq+T)bFb;U)yEWK ze@(gScH{qgLA#Yf&ffY4zkGEV!W7K;_b0sE=&_PZaH?z{Ym26M%)$k!j`AMQPn?@r zKc&=*C1{ef=eI3qs@h#cqh0(?pNT$G6H&q=W}%buhqd#-&l6MFcmBAnY`y#2+UvJU z#Gg&w`~UYL$zumu4qr8Gn#|$W)p*b~#lBSdtVc_F$HK{93<@`N9pAj~{2CRVABtCR zte4l`{(QorT(KR?HoU%Uch}1JkMn}lU!Q02&(C$at(j^6`Hjngud>F*n`|fTjGVIJ z@?Ccu7iKY+yB;&nI>)$%MVV!%b@c=e}3Sq z6*_ZGHl=tiOu17opRy}+!GEsVRy9RYAu5rdriZayns;hehS;Sfk@h&wic6PFF0!XE zW#9O+|Jz+hRt>X1v#$wUG2dp}T$muSPV@MQnvPEmy^O1QLRku~hQ~d0U`?)2of#2t zw=cG?UNtuEFI!ZD)T~zv_iwAaw_?TY+@y+G@m@@)FD%;RJ-O<%m51_yf(=oh>`yj* zIMnM_RQBd}F7vi6c3T z$&Pti->USVTb)u!J<+>5#92l*Cx2OH7R${Gr+0HMig({(DZ5XWaoNTI_S-i;q&7(U z^33}C;y~LG*X_>^GrYZgV}imJ=NShD+1nn-8E4#Dxl!@9&9c)hr2*@IPH0_K$;Dv! z*?7xAh8&aJ`410YHw$Pw!o+;ExlV6$ZNx#Z9Wux5%_{|bP5N8gMZ5w%nb($FJkK%t z%*HcpyJZUf1iBWe&hz@e<@m3YQHOYEOYC@RxFl%SsUp)WQQ?1kO>V}V&fRi5@Og6W zq3JnwI+;@zcFmH?Ts)i6ZD!IuQL~$mXK)9ot(f{~@`)#Bj!*Q9T(xTL#%;T#Gq$XJ z7M;0yZ{Kc)~9ysW#RSqBFLx{bv2LC~Q8Ddh|RG!&4^%!;NLX7#*=N*4cE-jD1d#>NWK% zC#R_g&iS-z_41v4Rnl2|W{O2;?{4B(to!A)FQ84D-dwarYJrh4t>!wJUg z8vS2Q-l*;5srAdbR=4!p^#b9u+pbhu%a&a$ExaMU^3b_^H@<4^>u|NEdZx&g;-Lygd9?#-@^Oc|CcbweIrL|5+fJN@ty_X#_&?9$k zQ|?8wpL^tE-tVCl&$I7n^HgnzrJNTfeG;$rp2**}=^%Tcp`XeoCXukBh=1irLsVBU z+r~C4Yw2FSOL<@ZOpp0{AVImu)N|X*XAu@L-wIELtj^tVRqwZK;U&xI?dEoY=dZ^4 z->~~B>32qSRAlp0oVqCJgdO>IYvOHRSss6XywM`1GUc!rvXH5Q( zlD5(H@JY6SpiQ@C@!kIK@1Aq%%a!v`yEa=kt&Pr@dDZMj*7U!97CX1j306}ODBfA* zm2)FMUj4>Z@9Lz0oSNs!2iN~k{1aMQy}R=4_IPcVM-hT^mwn!JPG(K5z@CR6UTySA zwmilqbK24`Bl7J{jvoOvTe}iEZP)e~A5y3-x$LOzyUVRRD$w0jbV`tmon=awiGIgdect1JO6nYxhU|x zxe??ecICBHk)H?C&)%}vrgy9xOPQAcej~B;V$Gs=on;3n8WzVsUMC!{ET9#)^V;uQ z;mz{b!rw|4O{%kBrZD%#0gGiB!4LO8b-HU~{y$oF)6y|fNnJuUYfH}+ zci#8h{|?iwmunIhUdvm&=xEpV_(drzFQuNEmc5{+wQH)DdBoD+)k~IVe{p;NEpYyK zU;D4s|4r=9i&&irE1VjhTzWm@?DaI+>$#Gz_cBWF3Y6ZjdHU{|v$r3uy>Tn|*0;Ae z56a$t8GGaE-CKWcZ=T+Jd-~C5d!!3|<8rs^pPwIlsp^}wP12hsZ(bIEX}HY4l*{1c z_4_-1zxcWP#r&l!t@RaIwRmMrD)uQlxAV@gKXM{)@$nwn?FW8v3ak6Z6`Z<~$?ZGe zu=rtQ_nCX`uHv&dA7P&MZtnXXwb{|@tL`tI|9i9S+t(iI)|Yp_d;8FlS>g9T2?>s; z@{bE#YVsz}m5%-X%%?L^;QCg##S?RKd$Q~f@ffc#nRIfibgO~EDeq_AGJaKITeGfq zEm~A1-lg$U;)80k&caUuha4(T&N*qOx{wuF|0uJVyT zl^^}xUTI}sa`)E}4xY}}`m;?ki_(^N$Im-)h$ZOTj|G=F6(kgQXjCUY-aSPZsO%d7iThk`}pKg;jRml(Y=Au zWxAp5jD-9j`<#ooWgf1uTXtv8mAiiJMfvIMuddDEJ6(Hq+U}P{>gPV6Vs^LO_x@zT zyTH8S?^c@g-IMx@_N4lXt^JqC^hxjbar5dU7q8C0{lwMvlk%MJVU6Y4Q%dZP$Jw*- z&gb~Y^~XwUqDb&xZlw>aB~otuoS!shAyem)39j*89~tK#j1^Rj%}QP+A^1ymYQp@4 zgH8XpZ4(y!5pa;dJjyz3MV|PFH5-oSoH<^!=Gdd!SJwrC?gg-yo?LlU=;MYpDjyEk z2Rzqrbb8;p$(pH3qi)mfW(R+zoeHX}47&urb*F79QIB65!nNXVT8@DDsQ||9)zu4+ z&lh;(a_!0l2~Eun)2yZ+dRLRw&oal#=zrXn$5&!ER~fBac0Hyt<=#&BeU|^0{Cuyc zb>aa3ypQYu@bM^p*!%J9{C(sj{D}K&lnsMJNeb!z7#_QMLtalAENr>nZcM)~;4BPa- zt955<>bLIH)ZFCk^!E>5JmImNJmEoy*yH>kZ%X({Dfi`op+%mD}2j zuTtLM+~_v>CR2pt?7I(3cG||xw-?}PRSqx_^H&Lec}8=a;D3`aovGTP&zb~94s%Xg zBsuLdtDr**XKIu8(wA+SH~U`ybd)&iIqRuwUk_WHJ-zGg@ye}!|LwXf&m39h z8h4aY=)?ks1x;*L5(!PLW)=^c+HH?;Idi5a>6o>z?)m(K$;qQj{}alU5s*j zExCRNn@{YK4GzW2j2C?7zP&wkj+bHQ35SrC9;Upyv75y^bbZh0tjSrsLMcdGwBL<~ zU1Q~}jHYJ?IkpSG7TUNdNq4D|)=Dw04Q@}k zyB4fDzxk5w)X!QQ_eGb#v)wag(uTrsFHdim-~aEA{=cSbZ=s#tQzfe0;x^pA_h$S0 zBW2F@T8dNlALoc)ne}d8TXNi#R4Q-Leb_v_#nj9`M zeo6WwY9JjjHIq%g=zp_=A z_dZw}9#{Q%ZTY>$(lIT6g({Pmwk$|CQuk5IG01m+Uey*V{&%@);#ukyJq!Hd{=~FYHfqu2-7VhFo_qfaEDaG}Ze7f4vfS(N>`kp&uh(tKJ?Jszsq93b zfE=sv*bCd3o=kbwD<0If$z{r?^ONcyhR>d{=UT(>i}lAV1drEjSvfVV>U+)4mw)R1 zSw8;c(b()G8X9RD65e|yCihC<-6;$2NiA6T=txO*&{A!kXu}5fgV%PMEV9h~l4ox8 z;w(d(QP)Qw#pyH8BpYf+oL1YmxxHEDv5EMOQ)}IC2Vt*rpH4{Hddwty zis0J$OFnmhshfOI-sqS2j{1~ECwNXcO%|PW)`P{lOxQBw)1wo&J8LSOSWTRw1n16( zJEV|0C05OHt$~1wtFg}atB*sPG){J{dfAq_^`@-f^ytP94^Qu_Ir_do@ABk=Up|#l zsuov+N~fhb*kw+B66Ai@ax2^9GR-`nS@QzZHC|pg(d>GA^DAEUxw9%ZzO3?Vp18!@ zSibMqxoneJCElMOU;3`^du)=NB%@86UWVDcFIj>|(%&o+6ZV`RrpUBx-t{b_$=5|5 z9DLn%&~)ho-lq?Ir#{fV`oPL!2P5abHn#T%lnPH~{9|RQ=RCQ;<59fR=jn!jM0M&# zkMHk!90}ce&jAb zmF^yzFCS`ezN+&1if^%tccrYGcYXhK$+ic*@(+CFADxv?o#t5iTD|hN_{VSlAN%}2 zpF96y-T6=D558}4`FZkw)xY+~LU-+!d?}kLu9}r{C`V>a%ojPE%8$)NH}ZZQ zIW2SV39G%+6}ti_IH%=*(s{Q&DND~k^fmXoWoKQoQcf?vdg&k5d4=?{D^!53IQ_&_8 zic|YDEFLM(+;LIntV-Xv4E4+sck`2r?b$pf7aRIX8HBhzl5?CY*XZcECi84|Fh%-5=FfII{B;aMhv~Z78!pocAw(bxAwJmbrN1eLGvze@GawqW{&JLL= z;2~)0biL-wj5XeYK2z5!F{QhT-0xby`cl8&zOK-9URPHIp3UNvR(rkLHzZd0!qf|l zyH?Csh&`jVwyykGA&{ zkJP&+xGXwpJ9U3g_1^RoU+i_QPg^VgF396cDSXo3YWTF>%f9Yo$8@#E`l+rz*Pir@ zc~;Xd`u~7~lvzfYsGN>!#RjF#U4JY2cFyrE(eqTFCvnDhreko&Wbd5#Ss=K zIqY4ulTLIy-udZaq3&dPa*bbDMjLNR8uQfF6X!maoqJJTw7KS+w^Zi56ADe!gX0W^ z|7!n^(0Uyjvr1$A$;)NmedbN=%0IQ6?@Q6W?^*X#w!GgpLA&-*^38z!gpY-XZvA=W zcw1=8e3?J?dWL%5as+kN$n;eMMvOR68QU&_p&A75cu zHZfqXu{eEznNbB_Sf;fUyoV;|9nsXpIF30-7SaQUEGwT zF7{>DJd)kda>PDW)3;J<%G9L!Ax{r$TL08qt+B_ZYx*8fzPW#2tT^0v<9?F;&#U>m z-#2>%`abE`IN>Gs_klS3oHg$?m;b77QfPj3W~-v-)1D1n>+*G9U%GpW=iS$N)BOWP zciMzJ-E>~^+x4Wo-iA^Fp30jf4mhw~oFDnDVY|hv8OoJ&%;PjSAI-aJ#@;r`V|%L> z>yH+%Ng963+@d5>BK{oG&d;2E>()QNFB4O}?&!KpJNKGieSPoKzW&;0)-(SV*qzuX z@Q>r%t~-k+3BP_kt9kKe?WM;GZ04@FpP4`7f7jZl4mSM)K5J#SfBvcSvE69dQW~6CmGj>c; z@SVn_nqZJ&!BYRm^!VX!|G&45_noaR)C@UtVv*3D_8_qst}pv+u$S{r@Vi>s&lM*C#&}d(_i{IFEKSy ztl{`B`7l^-(PomVO@ToA>&oLTGCn*URs^_6BE6j8g(`F-z?x<7M&Ui)by$TZ;u z^VX(a4z;rqO@3znVQbLHUCL%)5&NP{q3r8n`Ae-vFHSai+!b5$nxR}^_a^7#=Pw2O zH$4mAAeOg$cZ&Rzl()yt`2H2dd)j;Yo-o@PDBk36WVzzgeb&{N7~f2Okn;V^(akSe9;fFWjwi;Hxs*0+o89$%zY;_~*+a8cgBdO=P8p0DSs|9?I$UvK^O<*8}Xn-%`;KXXO( z=IWKzu8WKk%T}6gU4Pc=lcVN8{oDDTTqkw=XFdJw-XJ6zU@g&m_>vBj@1ct(HoVT4 zEmwV;`Z}PsFYwC>x9u5iDwp*87z@Ah8<}(_PLE}36E`wiDtJ39MCIUxHzKFYmReny@RxU&iOXv0nL7)v*w&! zw@dbocIHB(Oz-lJo5Jh29&Em(_1yIB%bR9d^Uuz*`*~uiv(QnsRw?fZ6Q`)Y?tkf?OwM;^y;xIeJ>MUro4Xi;@PXfYu&FOzkDt&Afdv;$vfHY znR4J>V>L59!-YvgGZy)-o)u`UZMt0UYjEHz|7Rrzf5Q3}+26aia>A~IFN3DeGc4)P zE6GZ}Br*Hik5|4P`B7n)H_x7Uc-p3I*Yct*t6y#1Zr3hVy2G&CN_GF^yz)!UQF~Un zi`EzaYQGsCcK_y|gzuA&niu|AsUt60lk_Fhram)x<+iDr;*p;_qQcgMs;t$%xJap4 zSZd0FPf1}S+gD6bb=A?$isTg&-?nOrscBU7lQj(?aat?L*qJM;@=A z6nxI&dDG={R__jvq_saRji-luD1dcfT z=S~t*a?m;CB`~`{;)vsHhdax@JMQV~|HnL`L4ZX_=p1Y3h0O|vzxWh5tE5zN56Y`l z`|;mmG753~VspY}hfs=I4BsY!b!?FrXDi%pxi{HX)UGi&kjX){Nisx@V*!_<{)(=D ztO2=7idQcy*5|nks9fk2V6YMvS#VnQ3H#+m#((+uDjEMvSCX;r=FmQnb4bddU4Y}6 zkkdgMrMLq%O`>9sOY}+#f;gq<|jDSdCN~pE*3ksSv7{^oZO zHSa$!`$7M^U$X6vlhyKzPO?{o_)p}vaqd3&d6~|KM?X~;oO%$t*YWCs=tD;z#0Cd{ z*!Rz|_Q3uOs~`NjjQ{VOJpM56vS@?*gcctKRyk)a4UWpaQER0FSU$D=ca+-Ik~BeV z;kp2sPmZhfMOt|lYz$!flj+h{dmEwD;vTRgs%L7xHCT!j4zoC`Sbyw2y0Kt`35?Nd3-icY}d!FTb*kU&O3ksX1 zbK5>foyl!Fq&92iRTWp?z-uP=maWb>w&iNnxwNHQH>Jx?*%r5x*DNz{v*#|Epqq)l zeHN)1%X{DVbe^+XzVvg{?!=<)J}>)L-Zft9e!V1@;qXgtTaL%&YL4QERMtO>=1z#x zeSN9hV6EZp?38z3*c!h{p0uuYoM61HeeO(!82C<Vyfp|`Y?Zjlnk%b!HaKnq~EcnKX|uz-*)x~ zDtCl^+o}&{?l7Ol#ox&N;X-@b!2)KvPO*c@f&LX-YZ{k3+ErNXVYgxablCVI^MlC4 z6-L&ZKfkhOw|97!mUr)s4bO=`XU^Y_WjMjD-%{Qn?xyUT? zTaBP;gZ@FCA4XX$@r^e>gkF)@$5#B{^$N}!=CniRfzCg~WE%1p2Y!&X;JEL)_+fQ{ z#J>Jxhxi|8{1NDFmp^Fu!*CXZedFT~!6t%rZ0--9XUP0v(mSYsQRateOiTRYn;)ci z@a%Ujepp_>T6f=sf7j%-ThHI)yCG-7f9TtX=NVJ>RvVotPpCfidP#rq+*kR_E${ND zTO77OSJuaWu5?}hx$;K)*cVRob6(#tsE~JmV!mOoi;wJqZ<8iY->~=6q#rSo`f>+D zUuPYbU-x*I-TT+d%k&beKR>R!vntPc*}Xa=C-I#5MRR(VUoURE826x6XrB1PGUGir zvPHhKE4QeYfNBo8OZ<%vfc1Kl*6+?%6xP9rM|( zo}Rt;rgM8mcly0oYvU?rZ`b_b_x{V3-Fr?KzyFq}|GipZU(@5Z@`pd_wrD;0;d#8> z;;5#a#nqMPtPe|mvp#OAXL)_+yWJOB-|d)ldgt?Nd2gq&S8tdt_fD|%ZE+|6<73(J zUrpcFUfy2wq1OJxa{1q#{dM=wS3jQqXVUiH(+z*h?s#&t)a!@jjwi{7^A9Zb3?c;okdYafNpb=jxaO!@U9|LN~6&H1vm`;RW27yof-!G*Y9 zQTYkRk6M|c&x+><{ya0Et&8oJ{G`>Mp{n7(WY%16+sIRrV0KG9*zfxm5?%^s&8;^L79shomTidNES+k?>pp#E;E7nCKTncBCO&*0 zdBRC^*RunMi_WggUc5$OVMw^uiYQZ_)A{GTR!n=Wi;cj}?wNFWtN=NBnY3?Br!WeW!QGesa5g zXO*Meib-s~>MLIxX|3?7t9`sf?d9uxvRiGZX(gzcvor0`Qfjm1me7)lUGT_@ht(s! zDO=--mFvTHzTVi=x?7!m`PX*e*KP8hD(G^FbJ zGFM`IYw((uMQ`K6&D-Tw_a$U{h*@~P+LoQDRM*J`PUl(#=Pi16yepqQ>Uon_ z)tiZZSN8w4`}Jh&SJP6VBiijJ=Iq#UeNAOw!KKI(d@J5`TsByK@?~qz9pjm7D;z`j z-hJEBR?yDkwe+QpbA0^$e=8^IEO!<9x6b&C;_Yjv_$L@IjyGIUAbx4y7r&JTOIkP> zEIbZhSbf4qyQpaACduL%9}L%6mEC$aGi#}j_nqU*F3MJ_xQorVIesbNSyXXC%icvd zM64&gysYD*d+c@XX952L#)cJ~rxh3?Yj)(SiJV@}e$nn}gYF|178e#LhIvv#D{l+P zPI<2^7cw!Vg_W^G((I_Cfe1tFK{qYdGqYL?_@;$${psk8zMrP4F|(r4k;iqS$4Z%o zwTVeReRZe(Aik$1^`T{mA^cV)g9}Ta{$F)dr#A9O>96gF8&~@F8PECY$bT>IQ|pDJelzXca$no~Ts3;a zd@^&^`*r0u$KUp*{a3l#eM!GpNG3A;-$F^d!1K9*I`uKL4tksHn|?Tf?OULW+x@!5 z{r|S^n<^a`Bd4#SUfrkOaYMSHDL6khB{!t-LDCwd*y(?hR;|wd_Fc&R;MJ2me0@%t zq;X8n&bUzQzgOa2cew4BgJ-60YX9ARW4CRCMM2tKLpH`LKksW5rUloXYR=96Zq2{9 z{OjDL|G(GjMN7}Oo$R#gSBv)F-jAto?OuyW&ACt&@ZgPX;4D7pglQEP`^j3pca}Ey!;j*e_WR>g zb>Ff`gc_`tU|s%kL(_x=UC+kmK5g5WBTfM+!E)>?m|}n56b@r#Ua)rFh2S%C8H#Na z`llM%Wh!1Y%jJ{{4Z6=EY1H69C4y7prIZK%W=oBVv@QRog^nzkDR#|#(;5Y3jZHOQ zHoo?k4EH#wrJx%hocHMEzQhbZh5Cw^2nVUknX`&_X({>MPb*%{>0rGmi(|sX;D)_< z=d-*_4&AUZefzs^_0j#?c5QJ`@H%9d(r)u6X(F4AVMuS2{L-~HnH&C2n=3doh>=w| z;;j3ViyO5VzbZRgJrK6^yt7G8FW{5-)`-P6e#a{2yfr*5;yb<>IPl^`CBe*53`d@4%zB9*|{RS+2#qy!nf=A?i`)=$C%OM z0H?E%;FJRl7a|2tJKo9cnent^jhbYjxl(Zb@9XbxEM2C3w4UQcQI}*|#-g`H#~SrF z+a7TVxOuKtM0k=)^oNBkUTI{$?ac+1b7lYcq$OrrE9 zxs4ZPS2z9G@7CR{edV%wRm)}XkjKpj=DZYSUAKRh=mv!qmmJ^DsxYsu?uaR!pY(iz zzTb&gW^?mel%tAI8*P(1D$Kc6C(V_G^H7e)`sj(WKL1~=mDkHTu*1gYW&GOdZIf#r z#=SoLb^n7MKI^0QKjfQmIrBnL`;7yPTO8gQczpC~Jzkpn=6n7GVa*=Z-#YWoyPSHj zP&ze(dG60?djuNyO&h)Bgdb#lE>L~_Y%=@Q+n24_YhKQ2QQ8`v zhZ%Zp8}>}z&coZr@b;z17v4A?t+3C%wPraJqQe*LbWmpb#{ZgU??2TDma4q!t>KA2 z>7SFf)pk&n!+BeYkk|6RFD-w*a%dNG zYhOM2RpndlU#yF^9<>S#TqMD`tSv3_MWrm$+4YV$^o_Z~C#7*8>v_M|_(hF{W|~L- ze#c{Cl`4(zk_}_Z)?YXIv!n3shQ_9PUbkmg4`*B{^Zh6pe(~9X#UWWo6OO(t<=t@g z)gmeW5R>d<9C>Gg_f%DR@hmfD-*xKceDk-DFP{5ewD$Da5e@mTuQ=Aqh_A2WY(G+&Bc5FR_XuWsw)>s7C~nXY`-<}rS> zx5D$x-q&g_DU+@BdUL1DR(|j#{_g!Z8M-g}`OWH&ujcfQeo$(7P$MtiVfK?>b~{#v z+36+U&DMQ(>4eDQ8mR?3&skTc+}baYrno#lZ;Ex&flG%<=eZy1+Bf&~36}|SQ4#g} zE{(>9(amnxRIWd*c*JvMRfefZ>+GtXjXOdX9?hFH?Z56FN7ltBR-C)m_~v%Tl)C}D z4NsMCZaQ4DR7_*)_id`@9QWBNZZ|&?T-or+p6zrTcjLTMd|yh}Nq)Y;6KOqfr{%4j zZ)HY)kI#Iv(k;FD;%>!+-rNan_p;4bYd_+hXSlFREUY^{mY1)ZL+iuUwuP~AE1k?FRujf2IB3>`#akFrJ`K1(Pjc=uVdZ}A8PaQh(wk^y~wBewZBCALr z&#V8v>kfCl`=?no`@Moy>XhR)Z+4~MsyvZ5uOfN*_WOUg#Uw1PT)g(z=^CT*)!(1w z?6ob)?2h_pwD`7ZRd?tF#r*RvW@TpL0hgyp=H`n2uz1sRo|jKm@A$6;t9ObnyY}z| zqXtvAwO8VqnCHg@xAo=yl#lCvG*|g`_hgZXx5v&*Zw~p+^CCqe>HaUXN58~gGR*L{6giZoN0 z&S%RtSsmQ*EF%8sQ*Dcd%jdZ5^M5%nE0-ZFTAE$Ndxb?_&fb&frKd#~uIRjbctL6R zwY_Co%#)8Bx~YGC zRng72@_g>Q`biHvYW9RNDSbQXdF^!GmviP<-pNmKzdvPu`Mr>!&8tG?*7MwTn$Uao z`1O3&?QB2tIiv2{ml{4^rpPX#nmMti;j1>onqB%j#n10;*XP-{=g_UjB&X zuzpT_0oIu>ev~q7X?n;FvrM|~RS?p~~!9mLCYNM(1<6OqpAhLa}W%6)i7&`e|N_ax)w z?1Od<6^RpU@5-)^)Xx0+KF4;d5JT-mStQC8>Muc!&N zsRDODF3MK$+I;KoO`+p*?{hw0i0@aM{L9DhsL0KhuB_>;({4T7Bt38G)0YcYaSESa ze%zxpbZPATl8XxSJ$r94mqZ0V{5xY(=r_({Vbf`rH$8VBUzWz%u(-Rrp||VX^)D|K z#0q$Cci1LfwodSi3dmt@-c&MQMfv;U3Z2f|ZNBzCc@t{Fs?YlBu9a3%N&Faj(BZ;6 z>HS+&QXbhq)1B8~qn&O(q35apR&A$4M;V$}+6_4PyY4;C()k%(zd^wzTw2gsb!vF8 z$HbQ>F6t|tjQq0vph@T}Va3FQ9#-em%3tgCJfEcv1r1?Yr8vVm(;Acy-*zbl_fSos`$_S z|9-1vCcHkB@AD^Y`Q*x9u19W*{x{iqM)&3Ac`vt{UTu8)DPzy?W%K3FOw~S;J=3f< zbmf0pCa<91+~P5ZJ~-aAa|t@QB1*zH=ZJap=AsR!W=K0<J#^{(OXKgk zGCP;dX1*D;M%C{6PLrz5uX)&ZoB1X!>0#>1U-kREMeI=~MTgVBBxlqI*H*XPf3!D> zmv3#|=1#s7JTuQs7OUU2H8b~VnZ9w5z`|NVkpo_r&i)U65*E-9U6Zz1>QX?{+36)~ zKgDfW^h=m$&!R2t+gVap|NYyLvh>47#(nqpDlWTGwzWIz*FLKae^v%YZabFh9NoP4 z_RsrF!Cmtke~JH#we9OqePSQTYWQIV&!XzLZ&GgFtDPlyHOX!MoMNf>^WJ6qeQ~`v z^K7`6m&JzZu~EhIi#8?;o21U{0}Uw`{|R_4*t{8rnQ`|S4r zn77J6@h^ifq+8yLWOG z-~aMxhROMDZ)7UsWNqUP{6E+e``UH#N6l5cN>aaYd&)*c#&w-n^es90dS{pP%vn(r zR);rlJRBXrQjzzzw5jWFEscM#ZY^3BKK=O8@NdjNHM#s#D&k^0BSLTZY8db&1|5A+8-g#jJqEgu(WC?yJx~zXv{ckE}eLvkD&f&ae%4+IT-& zv-n7dTip*gzrJFXO1D|=fbOZfAXCB*l9j>{d1AEC%$ZP)jRUzIIF5n3iHk>H#D<58x$NDXRJCW#~EB` z;C5f??ZRD4Hl#h0zwhv-Qt$1BoziQxHSg@H-pV5#$#6SZD)$Vto%=n5#pY}tStdg6 z>%KcK+!lCc-PNN#eHroqbL(JC^fr`zs@0xX#S~2cOc<%sJ-ac3loG z2lr_{7F@h|(LLAGU%g|~SO1zcH-dAM%lebO@03_qd9gS>SmU7_XE@`H=zG_#OA;@v z_^1|T-3B_2g})*p2l0;X9fLFZEa#SnV1roedU19aVBZjH5p+}#T`FlxSbo$_bzn!t8}p^M4UC?#VXsq?&;dHD=W{hw3e?( zm}j(Lquv#!iB&Oi`#(H*JLj!c*~~Ho@qPddk7EAeddB~h1S2ll+VF|nm3DQUZW`op68p@##`-nilXLQ-zAzACGU_gcOx zs}J9t*FMo^vW(|szZRpMsOF)poE6pOp!;gw`u8(Eetufw`NsE)ZU5R2b*WwZ5;`8Z zm}~zz|7p!?%Ng2R1drW*y=i0pb^D0v8+BFUeOi4a*Iz4POl;k0cllSYY|okfYt#jf z_C$#a$A4OQ<-&^(3)Fr!Cr%aG5q%~3{@owV>pHbP-zczMSD&n0eC)bQ@wS2q6@Qmr z)htwgBGdvlEVpx1i_`}1BZjMVa?p@(xuL_fu+e7rl z-x(!XObl54K*>Xb?Xf0Tj>%)gzjNaH%btrdZ|W*kbiStDxc_d&ch&7TdLO7cRj&>B zxPhTC%(IVod*GucS9O;~+r95FZ(YAzR>nH;>FO$0?$ot?GjlhK^u7qJaClY3q_EJG z>5T6pd7tDj(nBVs!Pr|MRcFT3`C zhiILzs!#v@H(T9Z!X50VPkJyl&CAs>IKCw8^@(s9_soR(%}W24hDR*v{FuirdUEB} zj^YdAY11b$XifTY!>*U(i~8TMYL`CD5_(p@pgist$EJU??l1gVWpl#cEz;}Zx;%?_ zZv?gar}^;htoa&h6cBRfh|bfD?Q);)&Skxr`o_C^{T{?4D_FhdWp5fXY&~#U}?rhDr23D@s@BAM96}oawv*4~rw4+q9 z*|QoG|7Wwsy0&WXf2||xH$mi!q0j2uf@LANw9!`9!eB|!YxF>GG z!CpSuhr9eYyf$-hYimuO`+3$Gt>f3r|9*TFSY&f2b6;ETj!EzL8<}^#FD*JZbz8*C z_h-&t7n5kU(q!d6t$1OD7n_M3OQ?hQQijQse9*al5{KA z)Z6UzC#EZ&{#m{$Z#vRg0&}9R_!S$iRz3;5qpc%RzJG1R8lP`guQ}_stL}8Exx*!| z-oYAlOn~{v5~h4Zj$CO`L5Eucy9-5)kEvZ+Su}5Q*}b=CKgg*n@BbrPdSp${uBNvq z8V|+oZc%t_e0Sx?N$#3=C9WA)hAsE^^<7%=T6F8L*(z2pKc!VaH!OMUmh&z1FF%{Y z|rNY%*UEcxa;QR#~lX;pwyFXKhkHGBF}Jt8m{2--;J&&)4;{X6*IlBk7KChp?)TohLyFe^w&YHrFz zj+m8|1yOR20+)3HLLK<(R91ig-6VLzO`v<~DHDeX#!w^A_-j_hZ9}?F)9Vxv?p)^2c56Q(lwqXs9fS zowz39&>X+5%jGBh{8pR%$Zp#@t=k%}g1`Tp7Bcs&*ssnR46Rp%x_y6oC2mwGd~Fu} zY4NQ)T|6;yUqY<8s$#@EHI?)tHzpkJ`q+EyeyFL|%&KMp?j{6?#{^>drM?YzHtwfF+(`x6^4`nYj97mD}iT=%S-UmQ8Hdu4mt z<^w(V3R#XlvdOXw@N7Q$*3`d@DRIG#88Y`5&e^}$eX*oOFZUF=+_rxrInKtlXPvyl z4>P`u+odm^Q!jdWy^@H%-Ccj(-4g%qJ^r$`zURC^mu;UzSk9^JjqZPLE`3({wdtyq zt258?7vhJ*S3dkbW9xtM^``$9I`vV%y~)B>7W{rrT*I^#Cf~F@GRs+P+XPMBrgo{->Xvr6c3a7u zIIOm4-J7tQX_uvpuM{0jzgc`;An3^q^+z_dpC{hV%Z}3ETUxTpsoK{}ZeFB!uKK0> z)2o>l{<@v!d9(Pf)nZ{m*X^qs&vO5)`ux=GOYO23*PYtVZ(85|_eav=1P1N*HNP8q zmnvMjAnGp@*L!Ww?L(Gljtc+h53(wtEEqrn}EASgb=SAL>V6D6# z4Pp0I@|h;y4(D`uQQ_8dsqpWsPuby*lyYt-?D}&4lE>3%-`)E)9^94vZZxSfcu`o& zS&d5-5}T$SW}5o;a@VA&+52{c#xoxeYxt$RA!doGB;RQ-t@8B(iP3F$_Pkx-x8?A% zVx9kDz5h16Ry2>hTEo}aE&pQ4$}1eJ4Y}WMUY0aTF--d8AH#pTXTI{p%Nq0TT4TNY z(WxXmyT@y%akSkPn|!;-y8L%k zr`ywisS3Mi&I#Ik>&W-OyFF}=7tFY~`Q`?RLeUb7sFmls|9B60OuG5zjmGEa>5 z-FkI-8{h1C$h&e zMxKl&O=*s>t*zbSng2I7O~_F4|2I?JBPF3Y-AB?TwkIvqnq$(h4}ELaY+AnYVpx{f z>YqXN4LuVpQ-n2s{S#eZcGp|`piE*$<=Z$fY3VaNYKopL{naeE=8Jezz;&5l61Qf| zNO1h?m)mJYx%lY!^ zR%qv^jf(F=*I2z#-xR4DX>e%w-%RVxQ~%$8HcvP^A=Bp0vPVvDb0=~88BScKwQJp* zQ@#qf6}1n^8b+;MpE)(T`r4LNOG;w;zQ5DCxTn-2_P{yU^PbhxR~+Z52pu-F6#aPg z^X&(Mos)}h_PU);D_Oyn-5#TN&+E{il>uj>0-98BE`M{nX6vI8yM>SUer9$~@2+i9 zUU7I<;IWg9c}{(f;(~LgSbCr0s111@rd-dtY|G|UfiryD?28VYJ#PG+{(SC~NaNsr zzwXUd(P}n&y;~;UGt~X#dG8zP(Bk|W|p4vY)$_2<9iiX=BH(;{8{m?|AykNitjP8 zf!pJLFWK_paQ&ZOEAB-loMOL}`Q7s(XPd>tsEZdLvs_!Yv&8WC!gv!?qx9E5-K^)% z?F_o}b^XTY!S?LNa~|c^94cR?<>#dpaY5*g=DBSz8xyv-X|?o~D<1uO$B0Xor1rK`72 zP3}>9xVF?~a?HkGKg?%8K6Y6!B>qKvz_+&{jmPhN&9%NC(`N7{f9fvD^y3Fv84Opg z<^K2SNiN$`=`T<9i?eql&T<-m_!ZbFr)J(+<7S?p$*DkV>MQ#`*3{tIMfZ^*$}v zQPGHYn{({&9OdwTsh7DbIoj1M^U`-O$WXoW`u5y)^8?)*ce}UQC|#Gk=e$+TZStvN z3)_{p51F@yaoTsU5zN2+#$~O_mV2+}^v4D-(w)2K$w?!TH;Y8~GP}N9VBKfz@JvEV zp|@iDw^J%B|NapE(;a*LT!-))J_}ubYuVDjJJnAwT>3!i{bs%iJ#Uwv3%7q9>9WJ} zW`E5)N&CLwk3Clwg`SbV-0)V0&E*9ElB zvbiQ=d{<%Tgvv!1Em$Z0{+fFHRn@op&_`w^l|{;iVG$Cun%KX0-FQ%Z?dTJUq|5X7 zWv?wiHBVb+!lR6Xd5s#aWvAyvzdmxLI9x*4Lh+`bPjZ}7M0Rc4wL=Sc@0-)i8Y$9f zcRl3MquNtXS9P)pUZ0m^w9@~0?Sx~l*8@LxKeCx`w|J9Gv(~!jneMu?rPe)aza78e zba!h0q^lo~)EVq}6Sk$-uSud!%i&$tY58?$KRv$Q@#vf7-Tx`s8ueEDpEzu9wUI7R zyTCf_{7$3At6BsTW;p5y`s(ot9rb+LyyJ$k>VYN?jawn}rrLA4+<#ZUPxQt8M7xT& zEN|3zulp%&HDy7i+g-!y3YQ(qB!umfRy_(iF#YZi)m*23y-7{pCv!r|Uu@mFba8^j zCE>kA5}RH|ePb}%H>c0!$4%FXnquvVUSj*1TAo*LNlIH7I5V?-n#i_AKiHQSgzpoY zzpJnL^NsIse@mKf;IZ%7xhgKL=4zlNzt24P=$y&xGqdfV32Z-=bTOsoZSAx9QvYo( zy?KB6KTB<24fFAx{4*Ltz5e>FKeK0zk)jU&md0IWrqfsbHPZjTsH|~A=UV5S>;=!R zuKJ>L>+giaQ|28DWJ$f(xX@>X_e!RTA3T%#P8ntG`Nb2=^XstBcAsHoS%db9Z!Bi7_!%Bp94%v0;=U6-N9J~SVhCf+lfM?>;fC7w zKa)Cw0#a*tvu?id@S=AQ|9sC)c0aZkbZ*j}wptv>$&dA1{qiLDyaJmA9BH;kj~|_O z_~y2D)3%O<0#7&J7OHrZE2wXu(G`25?F-Idqhb1zI(oyb?#wPaoE(zYcLO9Y-h+Ohn1>K#+xrhb9- z1uDu5c=sQy)VhU?nBqG#K)KB{Eb#F|*`|MZn_i_Vih-&`NqH#lu~yRf35 zMN(kdgKfgqFIOfDv?^^A_42*FXZ7i~JEjV4`*YhuXW#p_lsU~0bqw#l{qa#z$^Y@n zYYV2#JGp9cYD4CaN+0niDWz+*nQv#`kBgjjCvJN`*GK&y>`$j1C=cD*>UJf0Z|d=% zxAsk)7oWSsqv+wrn96FUHC>nHZM#~I~g)Pye7?5KD{D- zQFL^4h(sUj_7gVq*SZ$ovyWe~LV<6`h8?Zn7KSp5E*5-M$Zu7#G?vq~h4WR-`I^YK z{F5=2Nt<&-eyb_{zpL?FZZuYSY~pk*P(9)Ndv}?G zuf7W$;}t)rq%M7@Q_#lQ^~1-cCUf?j3yHC&vlvbNPdQc|Dq=vNB2EAl(VmK zUSJ~QR!g_(2Y1BHyZb6E?yg_#-$|7}jW11`E}!yZk4^H%^n^WionAf*C$TO1Z6ctk0e)ERHX1 z)u*13Di#;b)i&u|TeXux?{m53;|__}PJ7fXSM1;Sx_|2H{b_qE|IRztcR>2jLD_d9 z`Sk^AFFs!6pI3XmxaYTo`X1)Aqr1GeY;-vgVZ9~i(dEyrABAq`F3w!s`tiZLSr@1E zd?~Lw$I#jSS=oPI{R#C6nfsS0ad7P9-SvibezZZ#jyoqdu6)McoLM5Lv}kta^UHoO zF3tOUnn&^N^PAIF?o;@Dboz=3DhD13-d_>RRn#_If zo4dID&!1AN}sp>gL`3{;%=w_kR!ed_SXhJbe4csk=-pgggsuo-_3wF)NTh zxp7*D^2F24${SgZ7ynayGO2BuyZn`iP_r57P675e7A#3PJALoP-dlauzn=a1z|t?6 z?ol1f*Csr>P?wFn{q^eIJG++D3axhOy3p4BRJN>q`uSeb@@Y-iwX;w08_k&0mGC^= zM$24l?ljJ-J58VN`Gn+6c^GN#9MH{tzwq$amXOU!-G$jq@`rUkv&dOrFg`plN9_92 zkMYg@2IY6H>asqS-TrwlwYt8+aQ8Rm)t-ENVs*ckXQ}5h)vt=yp8MjQ!m4oB%&6U# zC8uxuZGYWRuCSfIG-IX2zk>9C9M6KDTYcO3bVB)Lo`YLWPZzIcxU=iQw>J6zCZ{V* z)|C~m>z%@UrElkcjo)Hx_}R|vv6R2F`?C8czT?GZsS*sn!sqw0U&_@Ie!TRi^3|oX zA8!>*JM&F|`6FYUdY#ILGTesHFethNI zt7%$y=4Ww9WwB4{JJQ=`zOpALN_mm-o3u;X;d8?)G&V@T`ylru!8CCl?^3m1pYxCA z`$_F$(LdZ&^m!Ref6~tS%wsnNa&?+C*9E`1u+4|3razoZ?pO#n-@hZe2QDT3ntZOG z`HkNXxl^SNKBpeusq|F#S|a-^;eE_f>sr=nH*=|cl?}VR{Wx>?^ECnI9iqcgpkq%WwXV z=QXJ3{k>c}@t>fG$Pc@U(=*S$p1&h{Mz#Fue%FwzeCkH$uXqc;UpmDpIrgLJn%sqc zo0m%Ov63pk+a@S-o!d zr>!U6i1FF8&fjG5(yYEy4tLU`DrQ>7{k%K9{ov}f+nL`gx0bh@y2q`%=Tk-eI8mQ}^UB-jYz}qamvmw{yZa9R)tGt8Wo?_@&zcrl`MPxbZt3$=yT9-s zpR<8$n_c?7qpHrEXWYE-^Uc=mS9dqbm~8jsyM9#c($f{ES1;Xg?G+BE%kkC`z)WPTD7^E3Z1b>GhXohZDlNL;BC`nlN2L z;Z@1$%hTiC;~2Ao>d!}YK494=e6DXn)#Bpmvu6vNOHS7Qt|>BS=hTjCb7!Z%e)Wd+ zUx@K5zjj+=ji^aG^mcyzo-KTq+3iP<|Luo|VmeNrYn)a8iN)c>yY}7ES2v_y@7wg< zZ^z4IFS`o6kln&Bw&yeiEKj*w5-C7|Gt^K@9-V~+lxV|}Ns@`iFGTGD6qkFZNVR_)ujt&zPGlBLqX`nX!cK=R4h;%i(ld z%X-C8vqcJTr?390k@S4tnYQ1ncS*}-_$e{-kJ!Sk8twk>!=_Rl z#uEp(EL7mza!P)Q!oGFOrQUpTw9)7~(yW@cPq0VLW#R-TF6Gt7r8@Nj+Bp>!Ek!b& zJ*ySxL_avNrYmE{vM;)GHzclF<$OE*S@V?uW+Co0-|C=%om`*aOc#@zY&@59!>N-y zwp~z)(DQhv;K233QFc*{o`~~9zxhwn_px%@hKnbbm+rU8aCLAw(R0JY>(mDazI`6T zF7p#T7qD>gPn+?D%RuIT)>9SfSC90I=3f$wYOTyPeXw%&#;u+jFJ4Xbx3;fcotB<&h1x1uqH?ZS-6) zBf|cOzuJ=1o1{}tZ<_3UsOql5eC_BHrwlY~WS{dGhpDkQ8?=7;daoc?!^80!L#=+i zQQR_?Nehhg%74{7WYS1De4}wn)XbT|Ukg+hh#b3kPvrW;%BX$c&R$WOZnE+0j)gN) zlOy%-rDtr{1HL(MP>3gN)@}r>_e6(+{@Zr7sMD=^Q!;K${Wf@zp+=9f4jAP znzf(+i)-Lv^;>=_0RsG9i^FqfTX8bg2(W)raGx(IP z-I3RPJSF~fJ8iluf_OaaI(Ux;c%PbnakAls+)|@&iS1jfSq|kF%rN_V(TQR5_KbId zv(|Q8?ze1>I>eMyzlFj3gUqpxx3;BI&M#RWU%EPW$w_>i zOoD6GaU(X}(@~`_{~XX$OJjdqR3pxJ%5UXm@{#sbJU-+5S!gYb_(x2G3 zCPy}(ds}(<@$q;2KF=4>a5E7P__l%5H0Lw=vwyv)u)WqZ=Y1Lya>>-w-z;N7e*PvY~qx@-3d`Uy`ty4CdB@3;DF zhu&_u`D^Cupa*U4A7czu=FaG4TdMOb&)2tfTiU9IG{69VFKsm?~* z)&88AZ!~XS><_8(DO1bB6ms~#8ZLZb_~1LIQ}()-659?8utyBFO6nWN7&?c_^xzH-Xs!p!S#Y+SONcXN0qGg8FHN;UCg4l?CclRuIc?f z^S6{_vWc{IEd?nxmWM_7S6knqkhHaZoJOl zx(_}kO{EU$GJLH%%9HP>a7E`x^0oUY|Bd2aa^=acU8b_97Mh=9iduiC>1cyNcaOxH zdmHV#l^6MBq&h!rs4`i6-ROUXu^(5E0i(_IJ)IwOrFRFf-FW=+**22}`?5BviA=dH zcIy+fYE<}Qb=Adp9=z!NTB`Qyc_rt{KcRnbDsUzqEBd>`v%-R7!yo;qRtK9?GZ_Ec zI|Vee{}C&DS{zzw$Nc&E*4Cwt?7D2+!aE*_IKI%#WKg}fD2>@a%zL@sfA2T>n>2US zL^FqYZmF@HKAvQ>`Xbtz2qe^ptJuBuCqA+Zw*j-Z`yKYF|)^ z8{eaCn%}hDWbQ1uy{&Vx#*>`}e@)+(Jg``LW8sQ>8v`wEc3G^dDlm@zXg^Q#sz>JX z8G(DAcg?@KsY0?~ZcvG~SZ`&?S5xkJ-A~`0irW{EwqMs?$vi1+O?<`E=%fDgl+EUE zXZbN_qU;o2`#sam6$~@d)<1i0dvF=wN@0JN*?O11%}qbeW$F1Maptdu9gz*12Xk*Y ztTY!@DBEVRQMxRkJu0C60BeES`X%d1BELDMJfG~iXs+M?8(@O7Oub5nxCh) z|MIzL%_1sj(&wc7zi3Zn+lTHoZd#=~78!}yKHPp|Tyzwi{Z!kr(wr8l-dU9#=Y*OE01^BI3HFbvQV;-0l&`ls#7k{14cOMwE_iGA%$4=%wuxuOGxqR2(mrr-Lsjvn#A2?kHyt*H z3$^q7J951$uH^lUllx{|D_G6w%QJClioo`|t=lH>Up#j7j=K4#YVnJqCssdMclUm< zqD|gK)+jDfW6^s;XPsU0ua)>c-u~0*vHq30FRon95pt5eIHxGO<-*bv-BD_nzx?}p z{EM=@t(S(B`=VvHs?t1G9p1X7*F#EM%Xizif&)o>eor(+-YwxR8Z|v8f=`c;y45*CXa8^`o zjSf#$_qzDDnc=g4Ub|{=y4P7H+&f@{QpR_ z(rRCIVAt`Yh%P4a$aNZb`~Kf&UUNZXlG(Khcdu+qad#=KWY<0D_wnAG-?B@6rhTfs zxZ~2PZ_{nNV)C_OgWMi%-udNu{oWYaM-yJhtlhso;-#ejcYYTVG`u&m$(n_B`Oz7Jr zE%hS*)CR}gnsVE z?9EM;G2uy3J*JC^5J{5Y_~V?pM< z#8|yq>AdS&aswPyMWxCP=`ntO|G87=-abp2V=Ml8MNIj<`^>s{;fXVE&u_Kd+i>pR zjd}XNxdYoo#ddWcRYertJ4xPn?Teo2MaS(5H5Bxyh?c_4ysMS$;2%`CEOX`}#fS z;0NhZH8sK0>I4$a_7rA%pSjDpr0cazoO{D$gZ+zdDu2(I^lY>B)48AbpObkcyX*Z< z$CJKWSKT@)RCHm0(nVq0BT@-HVK%%xOn0x?Na@c%rMYgAsP4jl zD$D-(JpJ~{SbdS%wuw_Z>dFuFPWHawK3}@iOP)Yl5Wda6K2G4-%E@1u zf1kVeggxj{%Jls)osu>i)pzmBI!`@X_4GEU!K#j5hgM41%QPN7?t4w^*Sl1|6;7-3 z4}Z9B{_+1`8}%bAg0`3My?NpNt(|gAce301|GFt@MyFSQ%V{{C7@y(bU=ugRaZ2g5 zhy=^~yMrER_B`tRa&UA1+9M%Pm7?3!^51a1w)B7ZHqKM=-)+vl^WUu9y!FVXJZ@1Q z<7r#&WT20fu9Cl34K6hT@ZqofilhV$UQXf6o4>L~SGxWW}{qcdz@83$fi|l6~ zz9%S_=X=d6`<7+UKR`h0`W+ulM?aKOdjlVzqM0H#VhRuCZ#xXR}XldL8_8^}lJ#h1RQo9lOT%zux6F z`xXA)Zyb$_cT3N?xK(pW?7j(kdoE|J{8>;rZQr+BAFTB`C$8Rfuf|ws>*>~#6+5SF z7Vn!Dw!=qiPkjH{FZ<*7_OS#Y`ThSN<)OOKUx zVvmF0IM3|(wmxd%94)!r#RmeC+;S|gb8MK!yfmKw?vx1~{R@gWRQM+S9|7H%sr_4UTmhM($R}$DJ4gbn=tI{3=~;p6zM*jEAvm#$0ZF6DN7Fb1POeL z`6#zh>6CwNefs3qI?1IUUTF*7OZy$U`-t@ho-WnInXFs3v#_cg&ada5Q7~s}LK0WZ z_hmM(r!Ef7TebN6-9+*B-q4#f->55oR2RDDFMMBn+uWB`(S;j5o*FGZI%&d@7kQtr zKNS_+dWGTiMBZP_f$d?>KK!0B|B27O13A*CJoY^L_&lE1^N85&qjR@Ba@jL=hD9=? z0Ml{T-HOIjJ)B~v1m2&Lxcd5{O5P`|OCGK}CHP-p`;=(2k1V0ai`Os-e-A2IAK!6G z&!FFE|DU6e!^3si1e%UzKV0Fg_;*$xZ{AVgJ9ElqDs_CmE4P1UPCs(vMcRJ`n>$Z^ zT6S+MryQ^;^{w?dnfq4v2wUO=_G{6HSgp#N5C2?zId#6#wbyGlC2`-|*LNae zvWQhWxAWbbcU}n=CeO^dp!0C9=W7P(mWuW4Z{zD$cKm+yr1ZC{Mf>4QW6L*u^A=sp zFrR(J{{>Tb`IHBmN4k46-1i@y_}2GU>a`hLReH)zXNbIN%QKwt%f6%B$bX5vPWM*( zinQdXe~<56FkRq0>xFe2&uhN%Z_Aen$t!Ek$?Lx!^X=(@y;nAbuuE@!-LoZYopH9| z685DF9{+9F#rsIpS+V<$-kKHnp7p;x`t5v<)a8k_i>eF6CP(;0U-i5UzMb&ktmUjX0XJ4}_^oqQ|Hu=KDO~l& z?w4nDO3pOezx7(|bDvqFs~o1)rz|zOr!$e~)6TEU5@I*=Pw3y-Q#SKVzusa0m74^@ z-(S1>F8k4$a{`+*)@x}_Ir>#Cc9(P78~OVNi)_m>4r^FA7=&fKUT!?|*Gs>-7jG=Q zeAMa0m4$yNe(njLm9-{E`|q9VU8%{djH>x9cCK!HZc=lxXZqgGH?8>*@vn6v4o+d# zI%uij76!B4>;b7=lX1r&wIp6 z?q#*0^fg0a?rN_c4)5hYNj0ZUy{13u*3Tt@bDq{kL~ig|S-i!_M0X>b^IZLhpESSD zH~X&1&d#x`Xm;KA7m5|G-7m4f~ zwVq&K%>=#o+O3tZj{ORpWPM3qQ1Xf18%@z&lYMX0{^Z+Vyz%CO;#wxv3mfyIeNJ$m zQgB?f^z!o+hBlM`?M&Eoz5R>c{xf?^UYmsT?+U9&`BWi|CBnT2eYSWh z8JX=)-L_<{j#I(%oTmO#?+1zhoPGt{?>k(5-fy0@-fW@o6`%G?Z0VPJ?I>TNs(EAT zX6ak(PlNA&aozgNRatN6c80n}m)EL1S;@f>TX$xqv@7>|`Gu~Z5foKaXGUKajNSZ{ryUeEYv&yl>)m2<>8k0dTUXtm}+ z!*Ty_&8xROS#|YUXrSE1-_=ZWMfV;t{85}Def-9f??o=`VY&q_ z5tldfY`xHQnCq;-&&c{<-t@$)j1$k?USeK!V%yFvHa3nai}F9JJl_^5mt19fzUwHb z@4E1h_iMj|@Jem?YqsxV(oO*zoc`n&32l#Ve^ao_FXGOPp~Ae ztuNhwe3C$H=svc=!FRQTlYU$3SAerNT} z7bZKGd8~~%SFV0F-pB2nrJfgfUTgmP&+crYIzi&dmXyY26~`B4*qH6p zzV>^b)!+FM;%}5a8#q4mKB;u{|0Kx1VnLg@Q0;2&OZ%=S$3DDJ)LtK&cx!*!9BYk| zbDhS$T|cjENXlGR$X8?~?(DupaCrvC_G!=U!(iqdDW-8V{;l09DTNXsnNd6 zM{lKEyON@)XmnF0NUOQwK64LS=&HaqL8hHkS9)*$kmsY>6~I2@*d7VSBR6615eXHutC2m0joFwHpWVUo)T0#~W32 z;o`mBx|jEFy>RfI>Fz&U6YSHhD?jut^wyg9V)jejSvgrRS60{@+`Kc>VEfY7$L5|Y zImsz><|IRzZraR>Id02Or)>BTsbf6<_KTvfmuL3~eB`+JvcRvr#lyzOr^M=C*%Jc; zn^Wf%F5Tt$#UiE1Ah6|rANq_sI7Qc64{T3oBUw#4ua0X3 zAK|(rch+Xr-gz#<@6NFM`g^MHcZ*qCpua&wElXtSuZpwJb0mdm7>X=Ba0;#&HrpJ=X|z@XV)p4bI0{RE5%lA-FI+Z zIh&MoTJ7-#oF>a=?s)9=-D~%$-FBvLV)L)Bxn#XV_<_f)nZ^r?)^5rUVO|;LW@Y(U z;Z*28#}sFK{q*Y%3CCC56j77-!0=aIcD-wl{L3%$jb6q7`gi22tX%cQQ*8FbPAt!{q9KIv}bS^z+&spfpZ~otX#&YMXyY~!@Z@#|6e!i&+e`~4m7UO2FRpHuFx(ezbgH_JxgWcyc8EL_Rt5!&p9OVfey#lh@fzP8T)6Rt&m(~dF<}w?F(j3*jXEXxLD{#XopyYM|5F}q~Ck%C)Q@t zM{ct1I`HpF`+lQkj(zK{AB#dcwS&dI{TCW@zA;VDkIi@Zj(})xWjGjOt$a0a#vOaD1Q2J z!)c}OI*BEs{y&+QJvUra6I^RQf8S>J|97<*{;s(ut@LoawZ)xVr$5g#i`xIJ`#xXM zs;e^FXP%ri?X1B{<4|j}JwB|v{ie+?6b=33Bjk1J=eiUn1@S5ynSJ-3UR>BMas6d; z#h1Ar9$8PBz1aUB>o)LA%y`AQvC~VkyJ=0*qiv^y7sVJd<{xaYT)6($j{{fTtLz5M;}eU`6m461>uHE2k&2u@S3~sR^g|G4^0$fwkOt=s_!qB)NTy=e&gTO zs}0u<-Y@(reoo*V-?N&!S2;IxrQSwc{jHUH>)de2XzoGRH;b~_YXx5N97?R>4Yix{ zTjONRlZeTc*WC(NYrlIiKjK)i*`7j?xVz`-^USu{)ot3Pw=sH3)wF!B_4}iEXK>$9 zYn}Ra&gGf)N4oFL*q3UkYh0F6SIWM>m~;DvJr-JRiCq1DYvO84iiEOm_fBuCNO`&E z7x!}c=M#G#U%h?Mf6Y6N&67`@$-Tw+Z5y|+B~yQlUP<+hrpUDH{+M&$`@Xf6>`F~7 zDp;B-ykqZdp^j>;2kl7@6&IJjIsNs|Y||At_dX3fI`L{${tCyFpXyXEt};9IbHTwZ zJCy~O{?*N5zIgt`{LI*g9c^z7jc01zpPN0~FD(D5`}=eM{%if6$SM5itgmKOoO`@~ zx@xv|{<+nw*mB=)`N7`k{lmrj>=ErcpO2e=DlOTt>ecFhF)<0^GIMl4UuP8#KXy*I zM|9;JDTB;R*|vHMpGO+rD?iUVwC987vX+jLJv}uaYd36-buhLK@{>L2vrlEV(=tD& z^J#%?g_B)_uO9v|bHBsAgRk`dnO~2*Y+3R(;$KUZQT8mJ1oqX|KX3oaF_3$?F7Nqv ziFqAyyLVNUd`)5BsrT*u6=_FP(F5-jzIy#jFWqF7us$xtR`Oj|%*Mr4%J)}wW=gBA z7XNWyiTPt?gGI#N-uEBkw(N13|9N-RD+!VIdrAwIoND^c`k?Ip%Qba17iIWa$~Npe zctGj+c?pQ2|c-OS;_hT2v zj4O*1dzbKNpTAl2;IRXF?r#=6=u`Eh3Siw=ku=?e$ zoqe*qmQ}tH74&=J#P{dohuxy8Rx()*`fY13=oY=Xs=Mf+K(0pMmKCC{50o9#`K*4* z|CZ?v`|^In65|Q+`Zm2$m)1%+RAg^<&E`pAD=9E~^qb4;;`LWcjQ3yEH2AzEiOsS@ zYE|TknsW|pl4nh;d0O1J`M=|vvDMIN_o|>-yi+r|1uvg-HQ)M3@PvPK;n^6U-S=|J zG}lem*mLh-L1S!aSoIrw*|izj6U>s!4{Vf;@p-rM@eRhy%7t^jm3@^KocOx&%)cFa zE6rD*nPzkMN${!Wx2d=Ho9*(da=gE`PopB}zUYI>*0zE#+Akh%td4rWm&ezAexY{l zGJ_+J{aRimyt^E8rM>XC9M`5`^k|{_V8+8*I5Fw&!LHC-dAhuMe*DHq}?n z;NaUJB2urIs1+K<6dTx97j%C;>pL;c1q<%n*X=pvx`5%xBA28)O$F@-?xJALgbz8PcA;)?%W*T*A#cnV_)WU{R!U_>xEZuK6S)s>b4`RLb;Fl zwN5PDJ+<$>_RHIDt5nJw`Tm;iU3f~vu<*nBS?5gO9N4^N8S{Uq`8=yimhPJLb%44e)D2Z+&y!P?|rCdX!Bj0gri&6OSws1)t>Sq$VRL$>?Kd?;@9n4Dre2@ z?b7%7@~bvhnWe?d);FWhy;htVbNsfv;PziKCnH}^dzXr5PC4TX)*Bn+ zZfvah^=KW_g@bP&ull`XVZ{Tj2To?nu2z9kp6=5^K3|#F`XI*CXOi`4vF4=;U$=3e z*45W`wm-SnD5BIWP*KcLs9SW|!h^rWW^jl+QJt_`=fLrs_9uj29!X0TUTDRzmyi2_ zv4-Q3vp1Tqa!+iLdQ{ZM=<)S^fjCdwEAz_PTW7xxj!TKU_vGyT)*DGm=j&>9174VM zO;5S@JZHn>+x%Y64rXedB1}{Hn1ht`E(hhkW~ynKHm_stBePS>=4>{*ZFjL#WJb~4 zDgDu9l?T&RiqBuT;yF$4rqsr%f7fo)nzkfX{^d^l!ir0$7q+xIUuXL{oi*0&ru80& zH5*qmoB#arCbwH&|5$p8@3%c{;nUx$3h`Di$)3@1Wn%rl9dQm1*KAXdefH{DiceJpfq(d8+Mi!~o9+{jMIM10H2TgkhaD^09oNXRLjLge;18{m zi}&u{#dp+4Wun-l+;8(VWj|;)%D!)vHJ_Na+hJG8QRbO14y*0Bza~)OSh(lyPT3`q zfBAn+uVV0C_Ta#r&M1+RA3V}h34hKTFTQKIRL4#&S5<_4qTWls!;v45gNtVQ!O#h`+i{ma^N4s9DUn++LsO}7GFtYsR+L6Wmm7-zrQ*E`@`N_AOmbK&r85=4*u$ zPnlI5B05})vo3sIF1!5uj(Od@{7ld1rqXxnFze#v3-G7Ylv@81#z31xt?<{xN7x+bc z$&Wb`-);Ty^XT@sU9UHl%x^ue|IF#T*w)oQIvYYV&)#aErZQD1dynp%xboX?*2)A2 zDGJ{B`9@%ef%nVB#%JRmYNc?^)mp#VqayRGc;ZPLjWy*nf3s~#`y1FY|87d+XRUdE zYx$n(3%;_t+V=6vmhV?)=qN1sd&K!j=)01?d#1C4-(w{iwRKy6 zM>4zZo_f@>uru)I}5e4bMkt zYEMZ2Vjf#Kb#?BE^$u@e*mw6?`;^Oku>UxxT{z^>eD{mZw|}%Olxk-8PM^Z`FkAG- zeFayywmoZ?1@GKulx4W>lHZKkze6UgD}-Hr&A+oJ?vv90Uq7zKZ9gWNllelndhez) zzm|toEi69nA82{wm1TR|{Ll9{?~6||;63@hGxkGp#*uVsDG{~XE`P&x>sH&oo7b$6|Jjt}%#iB}E0_j~JZd5$B(m39VE8=KGn z+V8FuoBHA1&syt-bjJLv8NVj|Tli|{#^XnxO<*vS=6=YVc;TVLjHP9Hm1kuS{Z_g4 zbZO2S&o>Vysn-7~o$Y14mosPg#s-U`k_*4LbI&=XQ6b54QNPwT$fgB4o;3k#kGeD%FMNu*e#p>h-7E|YzgKVSI1`&yrW z{QMy^uBv}$n`X>Ea`9xozlDukfcA;hh8la86IZ_O`+sP`@$0O6FxrRMV?m3z;tK9gS#e35WVjWE~ zf=j1HUG-YHB`h%dsB~&(YyHHI_KYP5!_REoD}4O%ljp%t<*P5u+Ec2qU`MZuK+ht% zv-fU%n78Xgi|h9@Ta<1oDzS#2@cNqb%hYDyrmMj}TOOHt9XP7~Z4R&Q<0)oZUt0ti zH*R}9`_CVDEsKLb6E}UYUAjv7YLD23C*5345=}?Lsvewn_f71NviG|s!4`4YLVM%= zKXMK+zUmyaCkcO_f0Fl~w9$q6kGr(GI{wx-ufF}8voLivGw1y~Q*NApY~?w@HZ$nS z>*VF07Jt(EB1}Aw{B7~veCZg2T7<-tsvHo%JkNx+QRm!3?_bdIK+8TYcVopj6 zOI*gSZN4=gMwc8+UmFV?d*N;r&!8j!h51+4+?}cRayrJRwj{8~O`IUVbn;wfhOXb6 z6yC0G+R^_xhMliD|Nh&+hiADfHh-`bJ-3A^{nOs8FEcK22^-&D!!=ugCG@|y6SI9{ z828dtWv9d8*PX=9biF^d;@7|3{C5udICSmJ&^lEr^88HkvCQ~|vf%}MKY4Y}Eo;;b z{vG~f-Sdw0N6f~(b(ISq^C|xod&47UWqf^)LW7_V|5P=dldO!_zg@e}eelyIrvz?^ zUAE;8UrT0MOnJ(i?p&!OKKVT7hQkU=W?l(?uFdnTb@RP|4VrVpB_8Z)67O^^6j^u2 zo;z#nt~nQ1yqmT5{fWcL9}7)X+Mc{yb)e7g%PhaiHz!0d%G}7^Y=1>^h1@}n;17IT zD!*V7-G&wifsY|Fy9?E4#6JlBW}Wm;GG- z*#F{%*Eu|gyT0r@vdJcP!MvM~Gq&pelC(X!@(TCI`MHn&2Fq(No4Z)>e*G*vrj+`} zZ`@{VOr9gRZE~%iwt35Bxp(u$e6O#!e(BNveTNU*n^)Bmc0ykIUuxalkA|O_R3&9> zxqVv9$u9=~)^_~!EPDR@+^;PyOSwf`61fk>9tw!Pb$^-Pilxm5H&pG3T%YxX?dsnp z_c>jc6mw2;h}8TUuio8a@@7?uKFha+gTj}6O*Wrg{q&Nq-7?mkWs~MI?iJJLxXLPc znprS^Uhb^uXu;P$Y*|`we&(Z@#5O7`q00mU1)Y})0q*G*BeaP=+Kzh9ZnFQ{jqc))+~($Whl zJZ_%BYwSKGdkS8?ChAjBWoq-xR4n!BlWuwVKr zel*wn#TPqK?TC^!+p2uHQquWrRxi0b&wS-tCE1TzTfC>2e(6d0R8;HYw4*SgcjN9& zjc4V{Cp9h2Z}DDnZg;|`%uB2$eO3A%1?sFXWe)D~tFX*hf3Mm+qh7GsMWE=kp}zas z^f-atjqH!EaWEg?_$9RaSL4UoCE0uDRXHD9ZgXXmDNaAsWOC4st@ZB{Zw`@!IQ@IZn!@@yHCjAKv+z=7av<4_dT!7WbT*K`#5x2eHJU3{B?HN`sJT*dFGNug^}jU z-|pRutEk%`VWTkRJin3P75?YEGu!Tef7iYKy@%1O*gES|EQL$?f@dZl6*?QLx3Bi~ zuH^a5w}XBB3f~L-6m{XT~Y=~U9;Zn<{hja2~9+Neh+_%t5jq_mY2FA(r z-e0Wx@a6m3L^iu=*Rp@SzoGH)68FPRS;vnrXh>KQ@S06o@Uut#-jh>;v)P>zKUd73 zw`59s&E$82E1j+h%_!kzy4iMC*Gk-PI(PQmX2tqNu}?1E^#AuO`reL^&K>-cd-)|k zT|eNX@||CvcYej!j{RH{bI%KhP72%Vdij2?%~$K> zccCXsb3N+{e=J<`|8`V{mce@uj@7%Z?@EUJJKxeC+Vel&@L&G4ch12oWn1H!C#b6I zPPXK@duGYsz&^&c`8KbaJ^i(}-jG}9oO3xVW}`$~&*$qmSDFTSss7b3*}Ku(_@WZq z=X>Vv)lvd0Vt=-Ne7D?t)5>)mdkV_g`|dQ?rx+zYei694F6ZFA$O-$;P2Qfzm-F{W zV0T)|Ms05AH=n06W*onAebdt?gOAvV3yP)?)3yj;Fg>-Xt?r&uHcQlu;j&@NV0_k7sy2&uN98U|e_dRsVs& zbp~eyosUI5uvO+ibMz|j8jXA5rac7-2OAIin>-feZ+zb3@jc`HhX=oUW;I_ub@9u& zJyu=4-7$Cib}Jp<7T|OK!y1m(bK z5jp+sZK4yV`e-Qiowmw;{Qq7tcc6d{n+oS+J2Te!dx|zug4aE(_xSw#-7EWwZzk&! zW{0|O2h+uvi=Vz&+|f6=z~!o~WNJHiV(shwvpJf6u5#M{b@SQ_2^TcyTwB)lnC+X9 z#)?~REYl{RNeIejd%vgke7RWJ^#9Ti|M)X(YiiKV(qP^9x8m6$FB`UKqaCXnuXp$x z-R*T$mPD@7KMXebK77w{~r6Gs<#x z4VB!vYEjdzlkwg+4`i-Th+X)wc=eupoc%DXltrrs{C`nJGFF8E5Q#lE$zQe6gjr#@ACW4pJIV_x=3ph4a z^>m)=w_P5y^Cz#WZCbmO;2pE3({GR6tBKqkzOg1!OnHS`aOx3f|AkMu+LQjQYe<{5 z?8gzIOXv3m{kq$^i>qDZ$NtbG99ciN2;ALPua>|58sg$ZO zI;#7_ZUhf~FQFH7*O`Y(SH?-Plc_=gA4A%B$+jc!jO~9NdnjKZHq%1jt6{|j_P@+K3-Ha zbGEpTqweCl3(|wwn<801&%R(8ly|b-e*N}7)$~c{7cXPud1=1%8hiD><;k_GReK$C zrSs}_e(w@yj}5lIIRDQCv1j`iPkGkgw=Q+1o=t?|^Dwy^GbKC9SU&AKT)FVf%qbZf z!Y}!rxmx}5cJZ9DlljWatF~g@3yV9qxz2KNbPV89)t$Y1{d(OkCu{KHQVpH1Hzy++KL zGej|L=JdeeWVTeNr4vm2SMA=Qu!f~)@BZG>kQM)yrL$aWTX6m1mTI9R&-LS!SEmLf z&tP@8OHScG_0&Dsy|5-+Jx{^#-ckk=(Kj0=q(7L|(h_4cb-H!4|B3wjhLX(>RZyd@P0-6@RNxXIHNy3MS!&#zB{T246w&yDv zZsEPSJxhUgQS~$fPo`}*ldfi!q*v5VzAPN`kn5e~CXF5CbJ!UIjDifzj2NW#i_BLo z_Ks55SBqb%xr41s`(E{|K(5T(*Y{pO`e=SkDP#HEwQl!=vi4riyZY*_-GUDsl~Ii~ zm%L`(>S1F(H6tstyj*_0(yG!eU2|B^OKs>ow_@p`jpjZl7yM)te73ulk>ynt+kUr# z@E2wOZXDsuQrMR7&;5S2bjMwL^;=3Or`5IJNYtI5Thl6fpXa0-cai>&<4@x=K6Z*f zygTWnT4(E`o%8KvUVc}6qU9hr&s@S&Cw`_p->)SrFk z3tw|`b$zj)1NAP?k9Nbq}sAK8}Cq_ zZ+MyA#mjz4z|v(?EsH*t&AgVSoZDuT+$_64LW+rb?c$dYtd5jKA1o<|-KSJI-*{8` zi;$G_BK}Gn56a%O&QRJ^zDj@J9*rHmX7SUmADHnqvO(`tv!Aleq|Vs^x4&(z@Vawn z!;-#@vQOAwztDf1a@#)t7DE$v&ZddtmeXyoa_tX^ivLuZ7SP`<`!u@M^6Q)$zTb-y z@AlVh^K|eJOBL1*VBNm?*{t_|>9YP$_(z49wuQpB4KhCYR@W%4FyKa;?PklV0UuQ>d>b(8s z!Bd}jtE;Fl+p?IuitS*gya1outCMy-%l&t`Nq&4+q-GIk@3_nFeM$Hp+jgZEhZmb8 z_rAY%`OZq)zQ4Q6?t}|XJij|a?&xm+wMULE!!;4e1X*_+h;uXt=!ET zYP#&nuPGnywJrRx?_=WrL$Wc<(c)U`RtIbrGV9!M{bhFgHoLeZlXyry|Ja z+qaPal9%ou6yfNTTb%d!K;F5>&buy!@?EjMFXubuH%A&n-}CK?!AskXGw&MsFI{{2 zok3daD!%yU${pdgfwHq-zFX1X?Rue z&n`XQq^8OJd*R{p2@NmKYNkyn(BWf~vfz4IUL=0u%`|_9d0o%eNU$=mjEePLbD{U? zp`Gg|I=XY}t&Q09?eA-i2}v)q9QD?SZ_j-a_fv1DFn{;O((Aixjwk46N9<+~Dsxwk z4{NdhlKw&1;Yn~hXMDko;9Vz=uS#xSW5wO~dusNfYmGT&vc*}#%fA?ed%n_nAaybI z-T9th;cEY@)YBS%t^C(A%Sr3!!nfbeE}Yeg=AHcXeB<6#j}uNtrSQLhwDXyjC+5lt2k%-iaoyF`sZ1yNg?4uN|N1HG zwN%tBc(%qQlP7Pa&h08S>p7-k(&y@MG4tp0Ewc)5%srW$b8z~UFXz`jjx4y7KhtBs z=llIF{_hMsW)| z>`$)T<+S^soia&r&CZEgJ68UeI^;AxYr(grN4^Ee$NoCH!*Z{v0(bCUU$ci*tS=k) z&Y$3ZN2TAng30BbZc5`&vB>OWprcwK-3S$8xg%6tQ0>;IVe664T194xf_U zQ?J;0-PoMt-e2p+soXmyARH>(3_ThSnQb1J0@FE;|Kiv3Tl2nSWvWM z?L7y}w%hL=H(t|>S9rJDK|WQ~S4UbTtZ&;39?|>Q#`U3U|gY6SY;S^qSB!i0bWlTK)L964}&uSoD5FTU6X zi!=@L?jQ1u(Eo1ylOy60M|ph?i=EYbuW#?X9}2v1lArVV-|M6a@fsKMuex`BopG(* z^w*)oe|LuEh#G{?7T1`@#&`PEv*Ndzc`x)UmBhlbzScgG{BZWZMVW+e{I$cs$JiU04^o?~pQ^O_ z2}zz;$!(u3qR+DQiRFC9tnx#jbGOb@4w|<~CB3<~+S2!$T=%j@s&$9+>Vi%y-YGzF5*}4l5dPm(+Vnkl!V`|4E|AO8DCLnt5UDaU>4QelEGO# zL(FV?uJ6X$`-L2PpQm-EB!`x*_!sur!`LB#^XqDE|I?u%s{bzgm&=H9DGM~Y{5tV` z%O#f`^GKFAvD^QyuZQIP$-lDJ zA2IJ+5#Gq2)WKfG|2j&uJR#BZ)?2A#J)1s!2;;Hdka}F%e2)H&m5WQ)#Yx?C+RAp5 zM|gH@wrAm?S>;C-Y?vVa*}uha#gQ#`JQ>v`hI$dNb6v9cdjIQM>X$9S*?#tLp0U#J ztCm-@Oy>6AekZQz_NzW+xa>vxu2uf7&Ayh$>t{=V<_K~LG( zoJrGH?cV$Q&18=73^|T2kDq?3F$r$^X>75zNe$hihg*w%XS_1}F|Lkm~c=T*Ic7(P8(~u6qg_E-t?5>KO3x=k|^Ju85vVIkPLeYSPPR zhhFBbk+$BT(RRc(O^bU=QSxHX)yuZW|NnCM*bWJmi^pe|s>c1?J#`h|g3H|1e*_#V zBm^9AG&yHdt-{u+t@rA?3T zGkt58W}os>!u-|@k-!dLop;%XW-(6G@w5BX7WDewrLBKXEZNn_U#Z|+do1Loc6q6V z=_a54Uyqd8kIzr^ejV$nuU%BOthC-#E=IXxQk9l%`lLNI*_z^YMlp|6N~5{cQdSC8 zt~$uF-t1QLnRjtI{}daK1&T^6+p^kGZI`6v7ftpB531h_xAh<<>npSUbJ#?oi+Kc5Rt6Q?i`yavXn)_U3 zv-R%e3=%y!U2rx4@gX$Etk2=W_`byL#M-n(*M-%B_W7$5iXyy_QiasQVf@ zQM1+e#!~*L8(C}^YV5Bv7JQoMZl3wQ>}%1(J1#ka7tfdFnTxEiyxNewUii}~XA)vtQG(!%1y=kTPm9?5Sr+@I&3-O$Q-dbYuw*B7Ou z*E{yrC{h~W(1I)dq`l>BnvoY{c z9>2cD-)t_Uf1wdp!7FxH`5&)r*nC0YPy?xcG+G48g7pF|^%YXLm*g4g?%$0g_PlI=wAK2J7`F?Qym-07iC83p- zXF09D&h)Lk+MS%=6?ty8|F^4gQpas`mV{lqwLWXIclWKjXXl?!zstlimAm%c(){DU zjTJriNgrKR>-g$(Qp=L^TX)|xt~}Ly;`M}!sj)xvS|(hZ_%-9l*J}#S==k%JU*QsjLf9y*L(w90W&3mTE zCWCdkBdf(P&x*LI&2d%ttG<<9O;7y!q}q-B@XYrP9EKZ%ViKocjC;NFneT1Ich4rb z*15zj4^d%QT6ug%Y1x;<3K61i^S3Csx?ewgXZusBqq9tSxV25#rFCCK72nvAtDJK? zS=x`|S-p^5pz5Oi;TCcXw-4S*I`jN;kJX?1)rU&&EOI|6F`*-PiXoF<;QZ5yuQ+$y zdM(nm=Aq1j?^~^-(*q@Rr`A^0rmnxZXu@orD{*`0-AdoY@UuM0;>DEf9Xemlrk+~$ z`2UPWrL{3e7oRjOI51(w)@d)@#gg-dUN5S=V^^yic`NF{o$#dVeXbc5H#uIbTg#qz zDn9%%$Z*+zK7l>mEzN5plH!+ZNbb95rtqa_*V>wP{ieBdcAnDMq|owo1J~IHN}oP7 z=PPWq>HaPjeoC%s%bGRY3MR_mesk;WwZGmpXPvkdf2EP1`Rz$HfSR zFRpOY`jr;x$b44%%5L>whkc@3U$f`z6P+)8j#>6olxKP!i=qgZa{u+u>mDW6@Xh(H zvS(S55|@M5<@wEPp3QN%`zn9RlLqz}4?(vHEo(xiZ?5)9OJDL>PC;?qpDSw*Ip_Uw zdbo+nn#pQq(V}zv810qhYVw=74V~B*sT#?BQ)o3v)XLhjaP`49A-S-~i@W1y-#)e` zhRtAA%8|a$yXQrF@4xwV($1yUecb(3bIwiu{r&&52JV$k?2Fzf{C93{O_-%68Yu82 zKRrHp;+ESRzyG~8+ilH<4X66z-m9N#aTjztz%^-q(Y$9{#opZt5&!7Lzw}Du4Mx6# zd8<gVk`%b**uHM zbm#V8eda{G->yI>ks9fhwcpmi@H;qnwx<1>|8l>?bk^QzSoS8o?ZCdM71z%Q-Z~U{ z^sB{d-JJVTXI8!Qf1k^iyZ_-!yPqw~e&2p`=%>-U_@M6Q4%ORB3Ix~auE;bv|E;F@ z<|C%cef-kedI=>lt3OPfl(;8*uZX8>@QE97T3p>L*X|Aq2*~ z+ruWt#soiI{mF>`oAu85$*IP#WEf8z*dVT};C|-l+U-oQu5~zxEO*ltb=Wnb$mv4D zhow;yi?5a}37<8?MuXooe)8ZZ1a& z*Do~_{>LrcoIcxcmT8buUV9+R82D6R4==Xun@kE;;S`BOapzewWnKm+^`0$c`;dn;%%qyFj?9 za8I@I%nz^$$<};Jp>{`bZ1c zfuxjG3|c*#SL}}He!;X|vi-%;d)IXT*yOJGVs9sjfj3qW^Z)oeTCuJemk0&`z5$*pPoib%_&{Exx#+~A<-#gvX z@=s)_7I4@v)|`BsP1E8~x}P)GZ|)1J zE#A{Rj~rEf@$K$*&53Dmy`QZ1uzR8W$i>KH?(J2cPH+F0Bp=XOFI%sj(sS;%FHgGo zvH3ec)NVfdqr*Y8R808B-}eQT1zSJRZ~D9F+kgJ? ztk8WG7=Awd#ly%j-$~QIyj<;nY4v3FTaC##HNSe+JmT>S&XN2-?+Wv=J-J2G%sK!4 zS*m&ZiP-Ewt?eshE^&Dt<({DO_|*w}<&Z>yM(4*{&o(RP%{ySM^(s~PWs5=(hZB2q z@)2z&<^&(bs2?s*U1py>!6q81onWjXcRsx2)%}Wxk6PPaCLCudI?g1UnDurAr%<>7 zd%k~)&Le}rUzX66=SegkFu>Zd#ZXcgs8EAiHwVP&hIli=^S$9T<}?XllIww!c& zd-?Usy<**Nxm!cNzWN$k8+LldqL@tauiwN{o(J;*L3v#VWGrTLO(uT-nsa(`W&mzPqN<$orx=sy#Dm%@!f3JO7<7E z-gZns5O?eNcfA9DUfEr_m1TQ&QhHI%{Bu_y`c^)3*n9Ae$>T47Hkbd9*eBhp=g{sr zFGO_OiB#Udmm=hL2|RW_*_a)%^atAn{i#Qv2!*BuNI6aa(WtNUX#bgy2F$IR6BRl) z>I%mxE#o|zcDTXN*J$r%+1S-Db@na#rBK86ddgL$bv2HrEJwOC(>e{D_RC%C*vJ{A z87Oe9AsNSX@!uGQ)J%;b$p@|GHV@%f#lK*cG<&&9Y02Lqlal zmxSkhR!vRU^^f?rbdFJ~0|c+9y!P|j<^!6n!J z+e*i6Q2KSL_`?ykQxD$PU%&kFaH(m=`{R~z?akGRj?0y_1!gOkZVG4)4=w$3K(^UC z`E9qIY47951?yQlUu=7|Uau+l?5)O{hx#0w;@fYWUwQfDdi_-kc}pHH=9gAwTy{e9 zvc@4_8{g>bCFRZKmz#86*tz*AxqPr)a?69)Hpu(XR{>?$#!0W6SZ&_PG1l`>-!3h4 zUX86IF~AW!F}1-(C15^}$KqPODAt<2QyMnOyel z>*2#wv+v2e?a})?x#swP2O+N=_f6u2Rc#9O)~|d}%3b<>(VELas{5Amd>fy zBA{5J@Tn-t>+br?YkISRt~X)m`xU^7ys4=2g?3mg_2)b(PDv zYh)ia|8>LDVMWxLgWrqJ@~2#IopQ;7-}TGu{np_VD&9Zu_g?f$AV#Bc;-T+N+gpw_ z2PQ@-I!+V)K`)&9~OATr177FMQvG2K}dnS6GyHE#JOI-Q?}ln6l}&gZCOl z?K1s6Pb8>8=mDqrBlXX3o5gQE*dbO~8EU`m)N#{T=^yjXD?Q)w9B#RhHv^KO;A z=lCsn{O|rr8w7tzx+RJ{+%$D+s!H}rvy|ohsy54tSdKGq>Ju&FWIoZoJTm=W&O?Jj z`J(~zwzMlAJIFac5y`Y9|&w{jiA-Mp_3Df1oEZe=Q%)I;E zeX6HM*nux0`6@;iO}n3~FZ}VZK9z5V^m|UtCmZcNCC(RhDg5?4$nU}~TYJ1HB6sPV zG>$bH21Wtg9EDEYHvgynw?7*0*H#nJ;Q7K`;qjr}l8(@R~26`~!c<-ZypFIZ{m_svN)T{YvkHB*R)fsWkLxb(mufv#u28Qxg8 z(urM0^r@alw!3jz-vw)p%>O67;wG*5BN@Z@C-moa4!1qN3r*)3CV8dH{EKiEsS-S) zVSaj3dc_3q1CM`ItFE>0ihK2VG; zY!3_kH)HdTn^6XPjpu8`e7!&IchntkaiL91IfYiN63*5Yt*xqcU4KnU?6uC6xV`f( zE#LenNAB`2^~95fq1R@W9LZYH6nedTtM7N=N15p{d1dFHFRk$okC3X+e6;vPW^~}>GQr4q=^VkV3wtYeKS{DU zw^8!m0pH)*SD)ms_UIM6QDPR!v}r@nRIb|_sykb^+_hS@iTl9f-3PyK`RJO*cy#jF zOHK0y47T2IdEsq%=Um(qqwCtH&+l-o;r#LGerLJ-<~{!e&1JaNgFWu+`(?B)PX05y z%cskBruiNLA;G72Yqc+2Kh`{_>D0W9GP)o3C32RpHZV8LGu@InYu7{8+1qt&n^}1O z=q9hee0$BsrE?Fx4>A9cweZVTI}yW;SIm|rzM1OHAG`dn!ZMF(rJl3?%Po91HOt5+ zUEh8CdFhScCV0FQk#A|PHQGXHp*(8T?d zB(~WyJrp?ib$i496vam;79{=u(=hc3C-6rFB zk8PQrmTSD}LD$qRjC|+C^_i3}KJv_t+^n^KlEbI66v5b|n|I7fc9UEyI`6ukLipGJ z=T<+OnRm{zW&MKrR}<#t*ETNOea|-YDv$eyzq{QVo)dgQsGo?UJtUUw?Mm{}`o6RYT!R+ujw}&TN0LI)Qci z+9`LJm+e$5eJNmlg2hCVjcKMxyy?%YuNH+S&Cc7%sTXp2S_Sj$``Ib$Kg~~We7re7 z^X})U-ha4}WuA5? zcHirnZ|b#eHTpR1R&A=)da)_BU18?tD;yu1me|j-TC^$M_N}s(wnvov-O!!;V!FDoSSHaA;IF*m>;7TvU zSJO3q-&K%Ow)JWXp1JX%*0&o;>P?e3cm493wf@G%D}8+1Ry#b^JN3u^h>FRJNs}t) z-8aii-|^(voe78j&aXXrT=DqYmsd@%Jo~guP)zCEE)kV&Pu&BGS1BygQB_;Gz}su~ znwRp0Qd32)C>GY3EPd^e>zp{E2xFa#J~UiFCwP`<0h!L;5W~tGf17>7Tl{e#wek9u8&_U$#kC zdG~TX2|3%bscS}yo~C835!bbwslsnRHl2T_u(Txc=oNjb#YzF<&9y#R?sN7|Z&^2S zmtcDGE|o3!n=hBTy3A#}(rxismG$kC`Aob=jvIeFdhq}ME$w{U+y5`Oz4Yxx{nq1t zA1n6Rew2TEHMaWgg1!It-`g=`dS?E-l)B7)U+W#}?2b&S`f+$-iHMEZrNRll^EPMR_fc2bm;9^ohG@ob1v%5y+AJ;q*lY zbKy7N3p^KJKfH9$bRh=zO+^P_KhX7e~zDj z{NXE$`pvJmCoDBI4?nGOe`Ck5i{aL#a~ifvlq{a>{B8G^gW<2QCOq19x_-wl=Cc;e zZ?yM4{b!$&aOlY->8}}EqWPB0jflB*YKZw1T0q!h-tDHZ7e28zB-K;NP?_IC_e0a;Y-`$c+KMKwKHM=hQQho006EE9cHbt(? z>dn~xDqWc8O2G9?@v+YT428eNo>;cKYW-u0*ofk@&%8XZ&N-xU`9!t^$JATaOA4d1 zmg_HEx_{TPnyp{{v)yIPdb=rc@4=X@u4)!iI}^Th?LYs#?57bsH(UMX_cq_|@7=OP zd6R+lf~Pkn<(75CaBt==Uf;8)vHji_)0?f{f!_6{yg!dK-N^s+)pP#~gBZ8Bp^kf} zdW)^nzt;GCqu3sU@V^`R!j482>0UWd&0+S(Us`m^mXz8RH<|x_;OCkv*JgNaqVnET zcE2VZU2OZqP-@z*DT@Mctq9A#Rr=z}(&+tyhhN^Z`stB)lPkk~*^^q?iIv6Cb&*e& z#hKQyf9t$`=V5hShx~rMl8_T>zQ^xIr(P^c-hcbRy0iQKS$IwpytVDxw$^Ant(F7p zzsrAoAglMvfAhL!(qvmdF3U{qM2;1HB&)CF^$G`6U}; zb}E01{a?asTFG{@sjyL8r7aYFZzFb;=Plf)!GJ)2G$It9;U_V)#YBq1Tu5nN9 zaqc0$Nj+{o>;_e6r2 z`0b;+TFX@4PP%jNrfO)JZ?ERWNohM0H!T&5PMYw(Oz(kR3G4IbpL?fv=(#iZ2vyf$`U(`A}(koNYi7H3;fGB?9WwDb z$Ism>N`-W}`RyhgPZfLpvTW%^Edht$Gh$Dy6@ zP-K(-?{>i{%Wda;Encn@+;dy#MX&RZ3&@tV-b zYw+yO+ZxqBENQQ^<*z;xk(#rytwTC7MX}-(`==dij(P?*?fsMNBy-V$w@H{u#cIX0 zKi{>2uN*v}WvHMdXlu)|$$!K4lR51cX_Jqna=Sg5_t{`#nT^D5)rqg(mrb~kogmDj z6#Mk?qBCslEz31}j6?PM_xZVTZDKntpdKi4Xv>Y26C<=W&+$Yp(U~rFE{E;9$+kuM z76%NI*Pcl1-u+*DLTk-~Pe)rfY{=SqhW+V(4;$Tw-V z%H$*KgXa}IUQl}O_%=b?aQ~xcHre#OGC5Ja@X0&TZClTau)Wvze>3ZrMrG4=oq8{2 zr>|R_ELPjB2?}d3%az^e7CB*u)~C><+llQSX`PQ%_1rukF$S*L6S(y2?De0TkCFwQk9yhuFYX{e|c6TBKq<8J^rUtX6&%hnr6|{ed&m}(2?zr zVwakqyYYA1^ncB!C-VK?tTRn-rCytR zIBIc>tn?N)vAT)L@{xkVskY946sN8gb*)j7uHL_F?~w&>l&0;N_IHVAsK)u^;!@VNtrj<1)7PF( z<#;~xl+})B^A4V_e6)by{?XR?-%mb#oFq^>QO&4qk5|p})qn0K%J8a-dnQC&3tjLc z>gVRvBTLjH>JyAql8OvpJoK@QzZbpvw|Uvw<4-aK3LV>Icl@2)dr6Z2x`*IIm77_m zJ`cPIs>hd&)Z&f>v{k*Ux>SLR1#3IT3U7Mv0eqXq8>F5M2jcae_WheH3y=S^v z_Ez|srE>pDf^S{8c&adPmWh$+U61v<0!n|Db6XlDU4E4w{pw7|QC4|FF8_+h6CIoG zZ{ERWKP5cOa{8h-x=Rar65R8zd&JDCs#?D`ONQH3J-KMdslCf@m^}F6*DqGiEG?W> zog19~{X46>z0k>9?~kl(ljW65_FcbeN!FE)8Cg4Nl16!>GgQ<`uQH=<<}={yB)dY2AVWPAcmeZ);^wyrT8{=*s6st7d%K zbaUPMnVGfkIymoM5{|yM{T0XLn>s#P$D`P%#(GIC6V|$6{e6Px)p(}lhPO}i5|WSP zo}6a2_22Wv%-lY{<-q9xe$!b0@k{>Xe=YR<(FKk=*-!TvrB6InjOp4g-43&Zsm zPoI48)V>L(hu%0Y;%S_?e?}FD!@3D4n^voc1t%V9x{$G^^&89aSr6n6XIaVg7DzMt zN8hPzG421TA~a_WYpln^{YSxy_|>7B}u^Q`TQ>OwJ-hG{aM9J8ev zc3VI8mPlkfZ7}7+aMMBZC@@P>X>D#9h>%|r2jR)(G^#N%YG(PxoYJfA5L;) z;LbJNST{X!mR!^)EB>|J{w;BZA<;~Klk}R|KXW{mWv}G?n>fH z;YT*?-BJ)|c8%r6oKouN)Wd->~kv)r9UB zQleo~E#*$%pV$ zA2pt-2;`sfP}wB=v^e6<=2@@P8-zH&SghO88kbdCvG>1=QsS;gTc6aB_1pbc^cR>3 zvd#)o)=)iRQ1z5k-jZv}imbu~DFW`UGk%$W{}3iQuap1meu1TJ0dL>?ulryAHr=~* zYSxU(zOWososdJHBb{Ezthz7ojFf6tlK$#g>vqF;^#{8kl1-_{^iesQU*m+1GhCx zaivvW3i`PqZH?g(8ReWk%bxwT(Y;ph`Fc8+!J?awRHW?ve%EQsD=drWy(d$}bQ>uZ$R<{QR#%nb4dqET1T0Yh-4m@$nS% z&9zTUU02&AF6?kLGLQ866p(d}E%vDTp6Q3RTC11;_tT8{zEvrT#mrG@b8mF0Q0`%t zvm1WB+8DCV%J1dkKe|^;XB&HEbS{zRQ!RLRv`MJt?kcZ~X^UPi)=11gtWmf4toE{^ z#?w7dcZIG@a^lhYBi+UBe{RG4!$MvSGaR|53xpocUa)B4tXN*g>{Cm)=UrRq*>&AW zxyZ^|_ekz%n}P`2y{eyWrt22(-6o*E;{1N^MrpH>tF;-vEFbQc*B_6Pxj)U%{Z6^~ zBD08(4hMeETpjS*)==TM@v0x+l5exeH7N8>%x%lP-=otUBbW5v;i{K}Rs_TMInT4V zZ{8iOa_>6x)AnJ1UCfO5ccU+Z4Ke8e4pRa^kpi)6$lHD2Agf->5Q?537=PcgZXv$D& z<=obJCRa=9$fMQXD^pC9w*S?Nko~jWK)w4*g3_vW_l;$Xu}hc%^3%rBN#@#_huJhkL&S#QL@@%Xi9fi-<{ zPba)AZ@eJncd@XTr7K9TYO?KqF2Ayz*rxLPTzXsT&)y6_TrBbIX8M`Zp1Idg?_VvI zF!`9K>B6SPGfIk{i%HyM>HYWTwPsq^v!+#BTpl{5dBjq^zeOE4>LrZIp&nypn;`OD& zf2w=oEz^fg}=CgSu!!z;)dYOf<+wrma1JX zTK8(->)s;fi^f7T+xuhuwzw9&ateJnF+fN2aig2ix10bM$EauewKulRJUYww(k8c> zf194``-T;&vfiIv@7An$b@z1B4)tF-k9E#pc@jL2U$u2wjbGCRy9}}tqPa&T-ZICr56!5ga zvemQl(u+fwSZZ}O)E8^Mmo%jSFkPZ%7P~F}-6jX`B3phwZuh z6A!oCx62eR`1|*CzEePc{Zn%jhV3>5T>5fRGBUf=Ss%<|=k}K?;?t~oq06}A+rl;V zHdicMAI z%#{7A?6ROl=-LklWFJ1&_sCO>n0t%miax{9cH84#Au>}kg7>z(TrS46+qFn>Z;-T0 zTkS3FPUj!J#uAmGxwf(%Q+;ilim%CpxvW@P`+L!qR@U%FbEEZF7ON~?U^wl@ot2^H zlephXzHDA3r{uS1LchRlF2Q}%8cLSQxz}F0<>Hqdu)JUQsE6cC!P|y9?E<;;R3<*L zo*HoRos5`9`{XIlTxJRC=>-2Vnc%Qwt6bXU`m!~Tc82y9zWa7WXK8fW^tQl^%aN}* ztz|DL?me{2Ly)KrOwM0^ZS=jVPS5J47m0Od(M91+b5~5F7)UVsJ?|M2VcFx@^|2%d-@+mU4 zh?rt^&+FLag8jh`m*T=-DBUdG;uyXzFw6CGx8{;>FHc!)_!TohN7PVq=jq~Q0-XDn zGC42E5Sex&YrCu0#)x+}-1j-89MSpK_iFK9(UgVyQ-8cz%hX@9LN#OlnOWcVzmcmw z_rBjWyS(qgT6vzG4-f>TH+KwH&c5V-Nq&fAZ=!~G$tedgtw_P*)Q}x>V@AbL-rguCR z_dfi#u0Pn<{PLr;+1Z}nv)|{rdM<67)i!Nia8=v7d6F}^Sd}_={ElE)<6u_C=ckt^ z7iahH(!2byXZgneoDS(dvt?QJlH`B z-?vi)1aGhXyX*Sh$E(+0-_6^@6T0czl*}vh6Zz~c^Y0x>I$ttr$D-=vva6me9k3Bs zI;Q43mm}EebC(%oTK2)ilMJk#I346tuUvHdp0+8u>izkX`e&4a&dq<&mDuv-_43d| zyZ;=0Ri}DeU+|yT{3Gk)J->SA?Efe`FI-8jhV$^bG_5dwm8MYc%MXv*{eHXgx@5X+ z@g29>KQ^9z=PFiutK#X81f<9U*KPLCBz)R-o)r~Sf{)EiD7ri&#p|-* zY(^>V97WgEWXGn;XTk?v`MbKW*SAg$i_GoadQCLR@tt9>S!D~q=TC3uU3+iboIh!r zrpt{rbM`bXnzX4)OMJ9sAwiNGI zR*_LJK4Tg^?PXL{es82$MAF(;vA6ueJJzs^v7{PBuQA(QR!~w@Rv8;++PW=vKX)Mq zYnZ0Cd3|{{zkhzbzWjc>%$M0lU*?(ZKk(jjVaTLYL9VO3Ojqh%U4HD!);H@|tG>OG zwF&wx&pl7f%rtB1I(OA<=N}$jUd3tWi+@^I9X#avS2;5?%C0ZGKrgMCH-5vlg;Ba5 zrS4f7Iazs}Ti9;jvbJh$K9t3nAOHWxCC%#IC5-keGCK4&biFJHfJR(+|knBY|fHNA)N znnL{gyVt1B{4_ta|4Uu(4|yh;u=wQSc$EpFC1HjaOAgo7D1Qr4d;iBv)GdHV-J>fF zG`+$g_^hi^CMK$w$upw%`Z^2oI~T1JOJmlbkYSNb;upBKQc7~mlPOmg?0zz>=V!^f z`%X7+eEXkz=d1CAsXL1_zgDt#JxMO zkx%dQBSC@pvzS9y&%ageGTrJ{W5C>$_>XJ{yZ;O^rxas@m1~hOU@otxaoCgQI((nMax>-8^z13X9S&c zU~HRoF{MN##N|2O-&1q)xDweu(A%ODx1 zV8@dhFWmU}mN#B9Tyyw(QOC?pwKEj2D_bsp7BzF@-=kc5pNkKR#+EbQW?!Ro=TYEV z&URJhQ%r$E9J3b$Y?^qq*pYSFLTdwtg@^67$ywdnKQr*=*JTy~vGcdhZ~imIbc@VG z>8_p6fPIyW@QE+^cynE-e+3$XNMi@x`m}vSXiD-?Q?JDZXGXbo06pzo)3D!>;}> zK^_iLR(Uh$Jv>$Jo$xTqbz^8lYr8Awnwr~Jv$k>a^r<{(V$3O;py2S%p6%O`=B^p? z-0qoYma3YWxFy_r^xye(SX+YtHS*x2#mZ9g_GwPidyv*Z(*B z!Y}t+djHR9&D#$mJb(4?I4qP;$=a#EDKyibZ{?|sZ@cf!>*4KGIdkQ!?yOykJ3?mV z>-`GUVpzv0qw;`}A(VkBM__Vd8^`&slUk>AQu1uOYbQ8opILsd?x(Z3#=pE1EAyWh zvLsqhzOZBVS>7Cm1q(JXGB_wQTb^)|<6wBC-Lv!GcR7aZ417W#7#g$~7|lA41UJ7C zH8S^<6tMWwS0~2bkj=o(S;62S+Q1;~@@S>wH?Enpm+)}N{8^k8>m}{C-G*1iTkulk zstp|`FO5=82~E{?UuW}^iIq!KGi*`F%p%bwt`T*KIO0F2vnE&mdQoxd z;LPk$6(@c-SBzUgnv*iOv)c`kO>k4w30zRKi2vy0f* zGP^v%ENXM}TNAUEef#&F;%~eCO8BJX)vEXn$8XQO`(sDTtG@wPERV8g?%B^&<+}1? z^|lvgn(r+#v%`#D%n=oRb!Dc0ESt2e0GqtTC+^E7Z%ZzQ?LQzS{4mGl=>om1$1Itz zRG9aCIBp^C;&Y4RCd=)f8%?*mZXVlap2(i5o*Jt3HZf<8o6_rpy5|j7S5}oxPVY{u z6OPzA!}kQEs=CfKrzYPw%@dfb&YgVX$}0U^;Yq{1;!lkHN3?pDRiz$F_XzR!l`+SJ$ss z4=(skxMk+~RN;2@wH~i~`lmi$>uqVtV11;u)z!xR`Ci*gyA(TSuDswOteCHQq2^(4 z@MGOenv+*9d$DHo`I}#Uwv=ApVtwaT^y4e51>zTVn`CcO+@yIr`f}01_Se@h`<7l` z@lt1_ZccV^me(GYTb*lbu0|$LkKnHUtNpTN{+be>hfDnKRb2n2c=z$%TVD?)|Ga88 z)2Hfd)w;@3n^~3h^4~PS{{D6~tg2o+&M`nBF)1*uSaIrNW7P%@;clbZ+$&|~YBIRY zh?sIS#^dRWGb>9Xmhg4l>SR-xn8&oJQ;em$;ldlUKnpmotc#vgPZcxQWaRFmJ#c6CwY7-m99?vNJ{Nc50v2D`5hr7Pk{`$->=yP23 z&is8pu61pHtCyz8Jt==C%S@-Gg_k9LN| z`jCmAw6P%*tP z63UzB`zKU!%!?M(Gq4B>yPGZ8HSzxU*lBTPm6jQKZr-n5WjA>*7nMqLYKd<5Q|f%H z^?1Ij;c~rldwI5S!!MH*^g^BG)teMMB~?`BPUqgs$dR>sY15B4SG?yROlogG#ThH% z`r^l5p`%Y8l$}au?Gt*p;z*9t+~QM{%%>uraOivuFLB;Un_jF?R{l=e zec8t4ks@3DLJtKVvf_RAM6~+#>fK7u_I2*oDqHh>tDV*F+O6;0LuH)`@=kwRFI|%O zHD_sI-gfrp{Y-H;w=iBa-@EPQQ(xvaxlwu&2bS%~x%|O?&li&`*Mc@EDD9T-shSZ! zd)58z50zU^s(hFp>b`6HYpYFxQ#zXh-MWKcrx$Ijkbn00`a92?`giO9TsX|`{PRk< z^1K`|4$-XQ_?DLk*=+<^+7cWSZxpZywKh*HRAb;^a^A4OAjg8K^N{0d3tTMwK8*btqo*p5V+;a#IY(+L_>^SA>gj7ut@ap3Xi1t-Ol+(Qe "static/fonts/FiraMono-Medium.woff2", fira_sans_license => "static/fonts/FiraSans-LICENSE.txt", source_serif_4_regular => "static/fonts/SourceSerif4-Regular.ttf.woff2", + source_serif_4_semibold => "static/fonts/SourceSerif4-Semibold.ttf.woff2", source_serif_4_bold => "static/fonts/SourceSerif4-Bold.ttf.woff2", source_serif_4_italic => "static/fonts/SourceSerif4-It.ttf.woff2", source_serif_4_license => "static/fonts/SourceSerif4-LICENSE.md", From 869bc2fded5e8b9332001da5a1ea252ca5f4f9ad Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 28 Jan 2025 00:51:33 +0300 Subject: [PATCH 243/342] override build profile for bootstrap tests Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/test.rs | 3 +++ src/bootstrap/src/core/builder/cargo.rs | 28 +++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0d3ab6ad97d3..505a6ef3e348 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -86,6 +86,7 @@ impl Step for CrateBootstrap { SourceType::InTree, &[], ); + let crate_name = path.rsplit_once('/').unwrap().1; run_cargo_test(cargo, &[], &[], crate_name, crate_name, bootstrap_host, builder); } @@ -3099,6 +3100,8 @@ impl Step for Bootstrap { &[], ); + cargo.release_build(false); + cargo .rustflag("-Cdebuginfo=2") .env("CARGO_TARGET_DIR", builder.out.join("bootstrap")) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 6b792108784d..4059aa6bcd28 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -88,12 +88,14 @@ impl HostFlags { #[derive(Debug)] pub struct Cargo { command: BootstrapCommand, + args: Vec, compiler: Compiler, target: TargetSelection, rustflags: Rustflags, rustdocflags: Rustflags, hostflags: HostFlags, allow_features: String, + release_build: bool, } impl Cargo { @@ -121,6 +123,10 @@ impl Cargo { cargo } + pub fn release_build(&mut self, release_build: bool) { + self.release_build = release_build; + } + pub fn compiler(&self) -> Compiler { self.compiler } @@ -153,7 +159,7 @@ impl Cargo { } pub fn arg(&mut self, arg: impl AsRef) -> &mut Cargo { - self.command.arg(arg.as_ref()); + self.args.push(arg.as_ref().into()); self } @@ -335,6 +341,12 @@ impl Cargo { impl From for BootstrapCommand { fn from(mut cargo: Cargo) -> BootstrapCommand { + if cargo.release_build { + cargo.args.insert(0, "--release".into()); + } + + cargo.command.args(cargo.args); + let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { cargo.command.env("RUSTFLAGS", rustflags); @@ -353,6 +365,7 @@ impl From for BootstrapCommand { if !cargo.allow_features.is_empty() { cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); } + cargo.command } } @@ -422,13 +435,6 @@ impl Builder<'_> { assert_eq!(target, compiler.host); } - if self.config.rust_optimize.is_release() && - // cargo bench/install do not accept `--release` and miri doesn't want it - !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest) - { - cargo.arg("--release"); - } - // Remove make-related flags to ensure Cargo can correctly set things up cargo.env_remove("MAKEFLAGS"); cargo.env_remove("MFLAGS"); @@ -1214,14 +1220,20 @@ impl Builder<'_> { rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); } + let release_build = self.config.rust_optimize.is_release() && + // cargo bench/install do not accept `--release` and miri doesn't want it + !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest); + Cargo { command: cargo, + args: vec![], compiler, target, rustflags, rustdocflags, hostflags, allow_features, + release_build, } } } From 0323af93b4fa4c816fcd400c5171d7c723c1fe18 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Jan 2025 16:48:11 +0100 Subject: [PATCH 244/342] Add new output-format --- src/librustdoc/config.rs | 14 +++++- src/librustdoc/doctest.rs | 77 ++++++++++++++++++++++++++++++--- src/librustdoc/html/markdown.rs | 16 ++++++- src/librustdoc/lib.rs | 10 ++++- 4 files changed, 105 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 80bc6cebd2aa..91f27166e471 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -33,6 +33,7 @@ pub(crate) enum OutputFormat { Json, #[default] Html, + Doctest, } impl OutputFormat { @@ -48,6 +49,7 @@ impl TryFrom<&str> for OutputFormat { match value { "json" => Ok(OutputFormat::Json), "html" => Ok(OutputFormat::Html), + "doctest" => Ok(OutputFormat::Doctest), _ => Err(format!("unknown output format `{value}`")), } } @@ -446,12 +448,20 @@ impl Options { } // check for `--output-format=json` - if !matches!(matches.opt_str("output-format").as_deref(), None | Some("html")) + if let Some(format) = matches.opt_str("output-format").as_deref() + && format != "html" && !matches.opt_present("show-coverage") && !nightly_options::is_unstable_enabled(matches) { + let extra = if format == "json" { + " (see https://github.com/rust-lang/rust/issues/76578)" + } else { + "" + }; dcx.fatal( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", + format!( + "the -Z unstable-options flag must be passed to enable --output-format for documentation generation{extra}", + ), ); } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8c3e28ecec38..48fe41c8b46d 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -26,11 +26,12 @@ use rustc_span::FileName; use rustc_span::edition::Edition; use rustc_span::symbol::sym; use rustc_target::spec::{Target, TargetTuple}; +use serde::{Serialize, Serializer}; use tempfile::{Builder as TempFileBuilder, TempDir}; use tracing::debug; use self::rust::HirCollector; -use crate::config::Options as RustdocOptions; +use crate::config::{Options as RustdocOptions, OutputFormat}; use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine}; use crate::lint::init_lints; @@ -133,6 +134,14 @@ fn get_doctest_dir() -> io::Result { TempFileBuilder::new().prefix("rustdoctest").tempdir() } +#[derive(Serialize)] +struct ExtractedDoctest { + /// `None` if the code syntax is invalid. + doctest_code: Option, + #[serde(flatten)] // We make all `ScrapedDocTest` fields at the same level as `doctest_code`. + scraped_test: ScrapedDocTest, +} + pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions) { let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -209,6 +218,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let args_path = temp_dir.path().join("rustdoc-cfgs"); crate::wrap_return(dcx, generate_args_file(&args_path, &options)); + let extract_doctests = options.output_format == OutputFormat::Doctest; let CreateRunnableDocTests { standalone_tests, mergeable_tests, @@ -217,7 +227,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions unused_extern_reports, compiling_test_count, .. - } = interface::run_compiler(config, |compiler| { + } = match interface::run_compiler(config, |compiler| { let krate = rustc_interface::passes::parse(&compiler.sess); let collector = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { @@ -226,21 +236,64 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let opts = scrape_test_config(crate_name, crate_attrs, args_path); let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = CreateRunnableDocTests::new(options, opts); let hir_collector = HirCollector::new( ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), enable_per_target_ignores, tcx, ); let tests = hir_collector.collect_crate(); - tests.into_iter().for_each(|t| collector.add_test(t)); + if extract_doctests { + let extracted = tests + .into_iter() + .map(|scraped_test| { + let edition = scraped_test.edition(&options); + let doctest = DocTestBuilder::new( + &scraped_test.text, + Some(&opts.crate_name), + edition, + false, + None, + Some(&scraped_test.langstr), + ); + let (full_test_code, size) = doctest.generate_unique_doctest( + &scraped_test.text, + scraped_test.langstr.test_harness, + &opts, + Some(&opts.crate_name), + ); + ExtractedDoctest { + doctest_code: if size != 0 { Some(full_test_code) } else { None }, + scraped_test, + } + }) + .collect::>(); - collector + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + if let Err(error) = serde_json::ser::to_writer(&mut stdout, &extracted) { + eprintln!(); + Err(format!("Failed to generate JSON output for doctests: {error:?}")) + } else { + Ok(None) + } + } else { + let mut collector = CreateRunnableDocTests::new(options, opts); + tests.into_iter().for_each(|t| collector.add_test(t)); + + Ok(Some(collector)) + } }); compiler.sess.dcx().abort_if_errors(); collector - }); + }) { + Ok(Some(collector)) => collector, + Ok(None) => return, + Err(error) => { + eprintln!("{error}"); + std::process::exit(1); + } + }; run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests); @@ -752,6 +805,14 @@ impl IndividualTestOptions { } } +fn filename_to_string( + filename: &FileName, + serializer: S, +) -> Result { + let filename = filename.prefer_remapped_unconditionaly().to_string(); + serializer.serialize_str(&filename) +} + /// A doctest scraped from the code, ready to be turned into a runnable test. /// /// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> `RunnableDoctest`. @@ -761,10 +822,14 @@ impl IndividualTestOptions { /// [`clean`]: crate::clean /// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests /// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest +#[derive(Serialize)] pub(crate) struct ScrapedDocTest { + #[serde(serialize_with = "filename_to_string")] filename: FileName, line: usize, + #[serde(rename = "doctest_attributes")] langstr: LangString, + #[serde(rename = "original_code")] text: String, name: String, } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7e835585b73e..7b4ed7a4d477 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -46,6 +46,7 @@ pub(crate) use rustc_resolve::rustdoc::main_body_opts; use rustc_resolve::rustdoc::may_be_doc_link; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; +use serde::{Serialize, Serializer}; use tracing::{debug, trace}; use crate::clean::RenderedLink; @@ -820,7 +821,17 @@ impl<'tcx> ExtraInfo<'tcx> { } } -#[derive(Eq, PartialEq, Clone, Debug)] +fn edition_to_string( + edition: &Option, + serializer: S, +) -> Result { + match edition { + Some(edition) => serializer.serialize_some(&edition.to_string()), + None => serializer.serialize_none(), + } +} + +#[derive(Eq, PartialEq, Clone, Debug, Serialize)] pub(crate) struct LangString { pub(crate) original: String, pub(crate) should_panic: bool, @@ -831,12 +842,13 @@ pub(crate) struct LangString { pub(crate) compile_fail: bool, pub(crate) standalone_crate: bool, pub(crate) error_codes: Vec, + #[serde(serialize_with = "edition_to_string")] pub(crate) edition: Option, pub(crate) added_classes: Vec, pub(crate) unknown: Vec, } -#[derive(Eq, PartialEq, Clone, Debug)] +#[derive(Eq, PartialEq, Clone, Debug, Serialize)] pub(crate) enum Ignore { All, None, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index bb954a31891a..44adf92ff0ee 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -814,7 +814,12 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { } }; - match (options.should_test, config::markdown_input(&input)) { + let output_format = options.output_format; + + match ( + options.should_test || output_format == config::OutputFormat::Doctest, + config::markdown_input(&input), + ) { (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options)), (true, None) => return doctest::run(dcx, input, options), (false, Some(md_input)) => { @@ -849,7 +854,6 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { // plug/cleaning passes. let crate_version = options.crate_version.clone(); - let output_format = options.output_format; let scrape_examples_options = options.scrape_examples_options.clone(); let bin_crate = options.bin_crate; @@ -899,6 +903,8 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { config::OutputFormat::Json => sess.time("render_json", || { run_renderer::>(krate, render_opts, cache, tcx) }), + // Already handled above with doctest runners. + config::OutputFormat::Doctest => unreachable!(), } }) }) From 7fa2094cb1bfccc37fa93baf8a45eb6ca24d43c4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Jan 2025 17:14:35 +0100 Subject: [PATCH 245/342] Add ui test for new rustdoc `--output-format=doctest` option --- tests/rustdoc-ui/extract-doctests.rs | 15 +++++++++++++++ tests/rustdoc-ui/extract-doctests.stdout | 1 + 2 files changed, 16 insertions(+) create mode 100644 tests/rustdoc-ui/extract-doctests.rs create mode 100644 tests/rustdoc-ui/extract-doctests.stdout diff --git a/tests/rustdoc-ui/extract-doctests.rs b/tests/rustdoc-ui/extract-doctests.rs new file mode 100644 index 000000000000..06bd35969d0c --- /dev/null +++ b/tests/rustdoc-ui/extract-doctests.rs @@ -0,0 +1,15 @@ +// Test to ensure that it generates expected output for `--output-format=doctest` command-line +// flag. + +//@ compile-flags:-Z unstable-options --output-format=doctest +//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR" +//@ check-pass + +//! ```ignore (checking attributes) +//! let x = 12; +//! let y = 14; +//! ``` +//! +//! ```edition2018,compile_fail +//! let +//! ``` diff --git a/tests/rustdoc-ui/extract-doctests.stdout b/tests/rustdoc-ui/extract-doctests.stdout new file mode 100644 index 000000000000..9d42af304496 --- /dev/null +++ b/tests/rustdoc-ui/extract-doctests.stdout @@ -0,0 +1 @@ +[{"doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","filename":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","name":"$DIR/extract-doctests.rs - (line 8)"},{"doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","filename":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_classes":[],"unknown":[]},"original_code":"let","name":"$DIR/extract-doctests.rs - (line 13)"}] \ No newline at end of file From 43bf52989a86b7eaa4ebac68dcac9ca627f5cf54 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Jan 2025 17:51:01 +0100 Subject: [PATCH 246/342] Move extracted doctest code and types into its own file --- src/librustdoc/doctest.rs | 50 +-------- src/librustdoc/doctest/extracted.rs | 132 +++++++++++++++++++++++ src/librustdoc/html/markdown.rs | 16 +-- tests/rustdoc-ui/extract-doctests.stdout | 2 +- 4 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 src/librustdoc/doctest/extracted.rs diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 48fe41c8b46d..46d0776699ac 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,3 +1,4 @@ +mod extracted; mod make; mod markdown; mod runner; @@ -26,7 +27,6 @@ use rustc_span::FileName; use rustc_span::edition::Edition; use rustc_span::symbol::sym; use rustc_target::spec::{Target, TargetTuple}; -use serde::{Serialize, Serializer}; use tempfile::{Builder as TempFileBuilder, TempDir}; use tracing::debug; @@ -134,14 +134,6 @@ fn get_doctest_dir() -> io::Result { TempFileBuilder::new().prefix("rustdoctest").tempdir() } -#[derive(Serialize)] -struct ExtractedDoctest { - /// `None` if the code syntax is invalid. - doctest_code: Option, - #[serde(flatten)] // We make all `ScrapedDocTest` fields at the same level as `doctest_code`. - scraped_test: ScrapedDocTest, -} - pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions) { let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -243,34 +235,12 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions ); let tests = hir_collector.collect_crate(); if extract_doctests { - let extracted = tests - .into_iter() - .map(|scraped_test| { - let edition = scraped_test.edition(&options); - let doctest = DocTestBuilder::new( - &scraped_test.text, - Some(&opts.crate_name), - edition, - false, - None, - Some(&scraped_test.langstr), - ); - let (full_test_code, size) = doctest.generate_unique_doctest( - &scraped_test.text, - scraped_test.langstr.test_harness, - &opts, - Some(&opts.crate_name), - ); - ExtractedDoctest { - doctest_code: if size != 0 { Some(full_test_code) } else { None }, - scraped_test, - } - }) - .collect::>(); + let mut collector = extracted::ExtractedDocTests::new(); + tests.into_iter().for_each(|t| collector.add_test(t, &opts, &options)); let stdout = std::io::stdout(); let mut stdout = stdout.lock(); - if let Err(error) = serde_json::ser::to_writer(&mut stdout, &extracted) { + if let Err(error) = serde_json::ser::to_writer(&mut stdout, &collector) { eprintln!(); Err(format!("Failed to generate JSON output for doctests: {error:?}")) } else { @@ -805,14 +775,6 @@ impl IndividualTestOptions { } } -fn filename_to_string( - filename: &FileName, - serializer: S, -) -> Result { - let filename = filename.prefer_remapped_unconditionaly().to_string(); - serializer.serialize_str(&filename) -} - /// A doctest scraped from the code, ready to be turned into a runnable test. /// /// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> `RunnableDoctest`. @@ -822,14 +784,10 @@ fn filename_to_string( /// [`clean`]: crate::clean /// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests /// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest -#[derive(Serialize)] pub(crate) struct ScrapedDocTest { - #[serde(serialize_with = "filename_to_string")] filename: FileName, line: usize, - #[serde(rename = "doctest_attributes")] langstr: LangString, - #[serde(rename = "original_code")] text: String, name: String, } diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs new file mode 100644 index 000000000000..b45cc907635f --- /dev/null +++ b/src/librustdoc/doctest/extracted.rs @@ -0,0 +1,132 @@ +use serde::Serialize; + +use super::{DocTestBuilder, ScrapedDocTest}; +use crate::config::Options as RustdocOptions; +use crate::html::markdown; + +const FORMAT_VERSION: u32 = 1; + +#[derive(Serialize)] +pub(crate) struct ExtractedDocTests { + #[allow(non_snake_case)] + format_version: u32, + doctests: Vec, +} + +impl ExtractedDocTests { + pub(crate) fn new() -> Self { + Self { format_version: FORMAT_VERSION, doctests: Vec::new() } + } + + pub(crate) fn add_test( + &mut self, + scraped_test: ScrapedDocTest, + opts: &super::GlobalTestOptions, + options: &RustdocOptions, + ) { + let edition = scraped_test.edition(&options); + + let ScrapedDocTest { filename, line, langstr, text, name } = scraped_test; + + let doctest = DocTestBuilder::new( + &text, + Some(&opts.crate_name), + edition, + false, + None, + Some(&langstr), + ); + let (full_test_code, size) = doctest.generate_unique_doctest( + &text, + langstr.test_harness, + &opts, + Some(&opts.crate_name), + ); + self.doctests.push(ExtractedDocTest { + file: filename.prefer_remapped_unconditionaly().to_string(), + line, + doctest_attributes: langstr.into(), + doctest_code: if size != 0 { Some(full_test_code) } else { None }, + original_code: text, + name, + }); + } +} + +#[derive(Serialize)] +pub(crate) struct ExtractedDocTest { + file: String, + line: usize, + doctest_attributes: LangString, + original_code: String, + /// `None` if the code syntax is invalid. + doctest_code: Option, + name: String, +} + +#[derive(Serialize)] +pub(crate) enum Ignore { + All, + None, + Some(Vec), +} + +impl From for Ignore { + fn from(original: markdown::Ignore) -> Self { + match original { + markdown::Ignore::All => Self::All, + markdown::Ignore::None => Self::None, + markdown::Ignore::Some(values) => Self::Some(values), + } + } +} + +#[derive(Serialize)] +struct LangString { + pub(crate) original: String, + pub(crate) should_panic: bool, + pub(crate) no_run: bool, + pub(crate) ignore: Ignore, + pub(crate) rust: bool, + pub(crate) test_harness: bool, + pub(crate) compile_fail: bool, + pub(crate) standalone_crate: bool, + pub(crate) error_codes: Vec, + pub(crate) edition: Option, + pub(crate) added_css_classes: Vec, + pub(crate) unknown: Vec, +} + +impl From for LangString { + fn from(original: markdown::LangString) -> Self { + let markdown::LangString { + original, + should_panic, + no_run, + ignore, + rust, + test_harness, + compile_fail, + standalone_crate, + error_codes, + edition, + added_classes, + unknown, + } = original; + + Self { + original, + should_panic, + no_run, + ignore: ignore.into(), + rust, + test_harness, + compile_fail, + standalone_crate, + error_codes, + edition: edition.map(|edition| edition.to_string()), + added_css_classes: added_classes, + unknown, + } + } +} diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7b4ed7a4d477..7e835585b73e 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -46,7 +46,6 @@ pub(crate) use rustc_resolve::rustdoc::main_body_opts; use rustc_resolve::rustdoc::may_be_doc_link; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; -use serde::{Serialize, Serializer}; use tracing::{debug, trace}; use crate::clean::RenderedLink; @@ -821,17 +820,7 @@ impl<'tcx> ExtraInfo<'tcx> { } } -fn edition_to_string( - edition: &Option, - serializer: S, -) -> Result { - match edition { - Some(edition) => serializer.serialize_some(&edition.to_string()), - None => serializer.serialize_none(), - } -} - -#[derive(Eq, PartialEq, Clone, Debug, Serialize)] +#[derive(Eq, PartialEq, Clone, Debug)] pub(crate) struct LangString { pub(crate) original: String, pub(crate) should_panic: bool, @@ -842,13 +831,12 @@ pub(crate) struct LangString { pub(crate) compile_fail: bool, pub(crate) standalone_crate: bool, pub(crate) error_codes: Vec, - #[serde(serialize_with = "edition_to_string")] pub(crate) edition: Option, pub(crate) added_classes: Vec, pub(crate) unknown: Vec, } -#[derive(Eq, PartialEq, Clone, Debug, Serialize)] +#[derive(Eq, PartialEq, Clone, Debug)] pub(crate) enum Ignore { All, None, diff --git a/tests/rustdoc-ui/extract-doctests.stdout b/tests/rustdoc-ui/extract-doctests.stdout index 9d42af304496..fa8604cae948 100644 --- a/tests/rustdoc-ui/extract-doctests.stdout +++ b/tests/rustdoc-ui/extract-doctests.stdout @@ -1 +1 @@ -[{"doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","filename":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","name":"$DIR/extract-doctests.rs - (line 8)"},{"doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","filename":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_classes":[],"unknown":[]},"original_code":"let","name":"$DIR/extract-doctests.rs - (line 13)"}] \ No newline at end of file +{"format_version":1,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","name":"$DIR/extract-doctests.rs - (line 13)"}]} \ No newline at end of file From 5e9e27e21b6363bfe8eab98f4aaa1ebb00a0f2f6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Jan 2025 17:55:29 +0100 Subject: [PATCH 247/342] Mention the tracking issue of `--output-format=doctest` --- src/librustdoc/config.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 91f27166e471..aa313af94ca7 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -455,6 +455,8 @@ impl Options { { let extra = if format == "json" { " (see https://github.com/rust-lang/rust/issues/76578)" + } else if format == "doctest" { + " (see https://github.com/rust-lang/rust/issues/134529)" } else { "" }; From 07c878910bd2243b67549808dc65e24a3e9c9528 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 27 Jan 2025 18:05:35 +0100 Subject: [PATCH 248/342] Add documentation for `--output-format=doctest` --- src/doc/rustdoc/src/unstable-features.md | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index d1d42e473225..7eb1b8df2333 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -524,6 +524,8 @@ use `-o -`. ## `-w`/`--output-format`: output format +### json + `--output-format json` emits documentation in the experimental [JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). `--output-format html` has no effect, and is also accepted on stable toolchains. @@ -542,6 +544,68 @@ It can also be used with `--show-coverage`. Take a look at its [documentation](#--show-coverage-calculate-the-percentage-of-items-with-documentation) for more information. +### doctest + +`--output-format doctest` emits JSON on stdout which gives you information about doctests in the +provided crate. + +Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) + +You can use this option like this: + +```bash +rustdoc -Zunstable-options --output-format=doctest src/lib.rs +``` + +For this rust code: + +```rust +/// ``` +/// let x = 12; +/// ``` +pub trait Trait {} +``` + +The generated output (formatted) will look like this: + +```json +{ + "format_version": 1, + "doctests": [ + { + "file": "foo.rs", + "line": 1, + "doctest_attributes": { + "original": "", + "should_panic": false, + "no_run": false, + "ignore": "None", + "rust": true, + "test_harness": false, + "compile_fail": false, + "standalone_crate": false, + "error_codes": [], + "edition": null, + "added_css_classes": [], + "unknown": [] + }, + "original_code": "let x = 12;", + "doctest_code": "#![allow(unused)]\nfn main() {\nlet x = 12;\n}", + "name": "foo.rs - Trait (line 1)" + } + ] +} +``` + + * `format_version` gives you the current version of the generated JSON. If we change the output in any way, the number will increase. + * `doctests` contains the list of doctests present in the crate. + * `file` is the file path where the doctest is located. + * `line` is the line where the doctest starts (so where the \`\`\` is located in the current code). + * `doctest_attributes` contains computed information about the attributes used on the doctests. For more information about doctest attributes, take a look [here](write-documentation/documentation-tests.html#attributes). + * `original_code` is the code as written in the source code before rustdoc modifies it. + * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present. + * `name` is the name generated by rustdoc which represents this doctest. + ## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) From a5d66e07e1775e3d5a9a8bb073fc50bf03101198 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Jan 2025 14:18:37 +0100 Subject: [PATCH 249/342] Improve code and add missing docs for new `doctest::extracted` module --- src/librustdoc/config.rs | 10 ++++------ src/librustdoc/doctest.rs | 22 ++++++++++++---------- src/librustdoc/doctest/extracted.rs | 11 ++++++++++- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index aa313af94ca7..1ba9dcaac1d2 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -453,12 +453,10 @@ impl Options { && !matches.opt_present("show-coverage") && !nightly_options::is_unstable_enabled(matches) { - let extra = if format == "json" { - " (see https://github.com/rust-lang/rust/issues/76578)" - } else if format == "doctest" { - " (see https://github.com/rust-lang/rust/issues/134529)" - } else { - "" + let extra = match format { + "json" => " (see https://github.com/rust-lang/rust/issues/76578)", + "doctest" => " (see https://github.com/rust-lang/rust/issues/134529)", + _ => "", }; dcx.fatal( format!( diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 46d0776699ac..8b522e614b81 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -211,15 +211,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions crate::wrap_return(dcx, generate_args_file(&args_path, &options)); let extract_doctests = options.output_format == OutputFormat::Doctest; - let CreateRunnableDocTests { - standalone_tests, - mergeable_tests, - rustdoc_options, - opts, - unused_extern_reports, - compiling_test_count, - .. - } = match interface::run_compiler(config, |compiler| { + let result = interface::run_compiler(config, |compiler| { let krate = rustc_interface::passes::parse(&compiler.sess); let collector = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { @@ -256,7 +248,17 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions compiler.sess.dcx().abort_if_errors(); collector - }) { + }); + + let CreateRunnableDocTests { + standalone_tests, + mergeable_tests, + rustdoc_options, + opts, + unused_extern_reports, + compiling_test_count, + .. + } = match result { Ok(Some(collector)) => collector, Ok(None) => return, Err(error) => { diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs index b45cc907635f..03c8814a4c96 100644 --- a/src/librustdoc/doctest/extracted.rs +++ b/src/librustdoc/doctest/extracted.rs @@ -1,14 +1,23 @@ +//! Rustdoc's doctest extraction. +//! +//! This module contains the logic to extract doctests and output a JSON containing this +//! information. + use serde::Serialize; use super::{DocTestBuilder, ScrapedDocTest}; use crate::config::Options as RustdocOptions; use crate::html::markdown; +/// The version of JSON output that this code generates. +/// +/// This integer is incremented with every breaking change to the API, +/// and is returned along with the JSON blob into the `format_version` root field. +/// Consuming code should assert that this value matches the format version(s) that it supports. const FORMAT_VERSION: u32 = 1; #[derive(Serialize)] pub(crate) struct ExtractedDocTests { - #[allow(non_snake_case)] format_version: u32, doctests: Vec, } From b7951380ca9e8eddcd67bf947b592d10e21bdea5 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Jan 2025 15:05:21 +0100 Subject: [PATCH 250/342] Improve check for `--output-format` combinations and add ui regression test --- src/librustdoc/config.rs | 66 ++++++++++++++------------ tests/rustdoc-ui/coverage/html.stderr | 2 +- tests/rustdoc-ui/doctest-output.rs | 1 + tests/rustdoc-ui/doctest-output.stderr | 2 + 4 files changed, 39 insertions(+), 32 deletions(-) create mode 100644 tests/rustdoc-ui/doctest-output.rs create mode 100644 tests/rustdoc-ui/doctest-output.stderr diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 1ba9dcaac1d2..cfba78952085 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -447,22 +447,42 @@ impl Options { } } + let show_coverage = matches.opt_present("show-coverage"); + let output_format_s = matches.opt_str("output-format"); + let output_format = match output_format_s { + Some(ref s) => match OutputFormat::try_from(s.as_str()) { + Ok(out_fmt) => out_fmt, + Err(e) => dcx.fatal(e), + }, + None => OutputFormat::default(), + }; + // check for `--output-format=json` - if let Some(format) = matches.opt_str("output-format").as_deref() - && format != "html" - && !matches.opt_present("show-coverage") - && !nightly_options::is_unstable_enabled(matches) - { - let extra = match format { - "json" => " (see https://github.com/rust-lang/rust/issues/76578)", - "doctest" => " (see https://github.com/rust-lang/rust/issues/134529)", - _ => "", - }; - dcx.fatal( - format!( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation{extra}", - ), - ); + match ( + output_format_s.as_ref().map(|_| output_format), + show_coverage, + nightly_options::is_unstable_enabled(matches), + ) { + (None | Some(OutputFormat::Json), true, _) => {} + (_, true, _) => { + dcx.fatal(format!( + "`--output-format={}` is not supported for the `--show-coverage` option", + output_format_s.unwrap_or_default(), + )); + } + // If `-Zunstable-options` is used, nothing to check after this point. + (_, false, true) => {} + (None | Some(OutputFormat::Html), false, _) => {} + (Some(OutputFormat::Json), false, false) => { + dcx.fatal( + "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", + ); + } + (Some(OutputFormat::Doctest), false, false) => { + dcx.fatal( + "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/134529)", + ); + } } let to_check = matches.opt_strs("check-theme"); @@ -714,8 +734,6 @@ impl Options { }) .collect(); - let show_coverage = matches.opt_present("show-coverage"); - let crate_types = match parse_crate_types_from_list(matches.opt_strs("crate-type")) { Ok(types) => types, Err(e) => { @@ -723,20 +741,6 @@ impl Options { } }; - let output_format = match matches.opt_str("output-format") { - Some(s) => match OutputFormat::try_from(s.as_str()) { - Ok(out_fmt) => { - if !out_fmt.is_json() && show_coverage { - dcx.fatal( - "html output format isn't supported for the --show-coverage option", - ); - } - out_fmt - } - Err(e) => dcx.fatal(e), - }, - None => OutputFormat::default(), - }; let crate_name = matches.opt_str("crate-name"); let bin_crate = crate_types.contains(&CrateType::Executable); let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); diff --git a/tests/rustdoc-ui/coverage/html.stderr b/tests/rustdoc-ui/coverage/html.stderr index adca375d4bce..764179820c5b 100644 --- a/tests/rustdoc-ui/coverage/html.stderr +++ b/tests/rustdoc-ui/coverage/html.stderr @@ -1,2 +1,2 @@ -error: html output format isn't supported for the --show-coverage option +error: `--output-format=html` is not supported for the `--show-coverage` option diff --git a/tests/rustdoc-ui/doctest-output.rs b/tests/rustdoc-ui/doctest-output.rs new file mode 100644 index 000000000000..720f2952980e --- /dev/null +++ b/tests/rustdoc-ui/doctest-output.rs @@ -0,0 +1 @@ +//@ compile-flags:-Z unstable-options --show-coverage --output-format=doctest diff --git a/tests/rustdoc-ui/doctest-output.stderr b/tests/rustdoc-ui/doctest-output.stderr new file mode 100644 index 000000000000..20c618dc61b1 --- /dev/null +++ b/tests/rustdoc-ui/doctest-output.stderr @@ -0,0 +1,2 @@ +error: `--output-format=doctest` is not supported for the `--show-coverage` option + From f47ad710595c1d87daa8f6b67045574c7304f83e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Dec 2024 10:37:37 +0000 Subject: [PATCH 251/342] Eliminate PatKind::Path --- compiler/rustc_ast_lowering/src/expr.rs | 6 +- compiler/rustc_ast_lowering/src/pat.rs | 33 +++++++---- compiler/rustc_hir/src/hir.rs | 7 +-- compiler/rustc_hir/src/intravisit.rs | 3 - compiler/rustc_hir/src/pat_util.rs | 5 +- .../rustc_hir_analysis/src/check/region.rs | 1 - .../src/hir_ty_lowering/lint.rs | 4 +- compiler/rustc_hir_pretty/src/lib.rs | 3 - compiler/rustc_hir_typeck/src/expr.rs | 1 - .../rustc_hir_typeck/src/expr_use_visitor.rs | 12 ++-- .../src/fn_ctxt/suggestions.rs | 10 ++-- .../rustc_hir_typeck/src/method/suggest.rs | 3 +- compiler/rustc_hir_typeck/src/pat.rs | 56 +++++++++++-------- compiler/rustc_lint/src/internal.rs | 10 ++-- compiler/rustc_lint/src/nonstandard_style.rs | 8 ++- .../rustc_mir_build/src/thir/pattern/mod.rs | 4 -- compiler/rustc_passes/src/dead.rs | 7 +-- compiler/rustc_passes/src/input_stats.rs | 1 - src/librustdoc/clean/utils.rs | 3 +- src/librustdoc/html/render/span_map.rs | 26 +++++---- .../clippy_lints/src/equatable_if_let.rs | 2 +- .../clippy_lints/src/manual_let_else.rs | 9 ++- .../src/manual_unwrap_or_default.rs | 4 +- .../src/matches/collapsible_match.rs | 8 ++- .../clippy_lints/src/matches/manual_ok_err.rs | 13 ++++- .../src/matches/manual_unwrap_or.rs | 8 ++- .../clippy_lints/src/matches/manual_utils.rs | 10 ++-- .../clippy_lints/src/matches/match_as_ref.rs | 4 +- .../src/matches/match_same_arms.rs | 8 ++- .../src/matches/match_wild_enum.rs | 11 +++- .../src/matches/needless_match.rs | 12 +++- .../src/matches/redundant_pattern_match.rs | 36 +++++++++--- .../clippy_lints/src/matches/single_match.rs | 21 +++---- .../clippy_lints/src/option_if_let_else.rs | 10 +++- src/tools/clippy/clippy_lints/src/use_self.rs | 4 +- .../clippy/clippy_lints/src/utils/author.rs | 5 -- .../clippy/clippy_utils/src/hir_utils.rs | 2 - src/tools/clippy/clippy_utils/src/lib.rs | 25 +++++++-- tests/ui/thir-print/thir-tree-match.stdout | 52 ++++++++--------- 39 files changed, 273 insertions(+), 174 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index b932915dc298..1267281f73eb 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1391,7 +1391,11 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); // Destructure like a unit struct. - let unit_struct_pat = hir::PatKind::Path(qpath); + let unit_struct_pat = hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(qpath), + hir_id: self.next_id(), + span: self.lower_span(lhs.span), + })); return self.pat_without_dbm(lhs.span, unit_struct_pat); } } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 3c78ed0497d7..cde8ddbfe03c 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -69,7 +69,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); - break hir::PatKind::Path(qpath); + let kind = hir::PatExprKind::Path(qpath); + let span = self.lower_span(pattern.span); + let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind }; + let expr = self.arena.alloc(expr); + return hir::Pat { + hir_id: self.next_id(), + kind: hir::PatKind::Expr(expr), + span, + default_binding_modes: true, + }; } PatKind::Struct(qself, path, fields, etc) => { let qpath = self.lower_qpath( @@ -304,16 +313,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } Some(res) => { - let hir_id = self.next_id(); let res = self.lower_res(res); - hir::PatKind::Path(hir::QPath::Resolved( - None, - self.arena.alloc(hir::Path { - span: self.lower_span(ident.span), - res, - segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], - }), - )) + let span = self.lower_span(ident.span); + hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved( + None, + self.arena.alloc(hir::Path { + span, + res, + segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)], + }), + )), + hir_id: self.next_id(), + span, + })) } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a396d705cbb6..af2f86b67e00 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1437,7 +1437,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true, Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), @@ -1464,7 +1464,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), @@ -1618,9 +1618,6 @@ pub enum PatKind<'hir> { /// A never pattern `!`. Never, - /// A path pattern for a unit struct/variant or a (maybe-associated) constant. - Path(QPath<'hir>), - /// A tuple pattern (e.g., `(a, b)`). /// If the `..` pattern fragment is present, then `Option` denotes its position. /// `0 <= position <= subpats.len()` diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index b0d80d0f8092..31764ab12094 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -709,9 +709,6 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat, children); } - PatKind::Path(ref qpath) => { - try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); - } PatKind::Struct(ref qpath, fields, _) => { try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat_field, fields); diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index bb853985c7d8..8dd1e4e82927 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -105,7 +105,10 @@ impl hir::Pat<'_> { let mut variants = vec![]; self.walk(|p| match &p.kind { PatKind::Or(_) => false, - PatKind::Path(hir::QPath::Resolved(_, path)) + PatKind::Expr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved(_, path)), + .. + }) | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) = diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 83c69dc2ef41..d43c65c0023b 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -703,7 +703,6 @@ fn resolve_local<'tcx>( | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Never - | PatKind::Path(_) | PatKind::Expr(_) | PatKind::Range(_, _, _) | PatKind::Err(_) => false, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 539c5f6a20af..44f7a035a10f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -41,8 +41,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) - | hir::Node::Pat(hir::Pat { - kind: hir::PatKind::Path(hir::QPath::TypeRelative(qself, _)), + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) if qself.hir_id == self_ty.hir_id => true, _ => false, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index afc0f627f696..a91afa51230d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1906,9 +1906,6 @@ impl<'a> State<'a> { } self.pclose(); } - PatKind::Path(ref qpath) => { - self.print_qpath(qpath, true); - } PatKind::Struct(ref qpath, fields, etc) => { self.print_qpath(qpath, true); self.nbsp(); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1c828591bcbd..f79667e59bae 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -480,7 +480,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::PatKind::Binding(_, _, _, _) | hir::PatKind::Struct(_, _, _) | hir::PatKind::TupleStruct(_, _, _) - | hir::PatKind::Path(_) | hir::PatKind::Tuple(_, _) | hir::PatKind::Box(_) | hir::PatKind::Ref(_, _) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 1f48b703e4ac..d37c68f82fb2 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -11,10 +11,9 @@ use hir::def::DefKind; use hir::pat_util::EnumerateAndAdjustIterator as _; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, PatKind}; +use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::place::ProjectionKind; // Export these here so that Clippy can use them. @@ -564,11 +563,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // FIXME(never_patterns): does this do what I expect? needs_to_be_read = true; } - PatKind::Path(qpath) => { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { // A `Path` pattern is just a name like `Foo`. This is either a // named constant or else it refers to an ADT variant - let res = self.cx.typeck_results().qpath_res(qpath, pat.hir_id); + let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); match res { Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { // Named constants have to be equated with the value @@ -581,7 +580,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. needs_to_be_read |= - self.is_multivariant_adt(place.place.ty(), pat.span); + self.is_multivariant_adt(place.place.ty(), *span); } } } @@ -1801,8 +1800,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Path(_) - | PatKind::Binding(.., None) + PatKind::Binding(.., None) | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Never diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 928010c03c2e..aea2e0fd3a30 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -5,12 +5,12 @@ use hir::def_id::LocalDefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens, + self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, + GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind, + WherePredicateKind, expr_needs_parens, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -1422,8 +1422,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // since the user probably just misunderstood how `let else` // and `&&` work together. if let Some((_, hir::Node::LetStmt(local))) = cond_parent - && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = - &local.pat.kind + && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath && let Some(did) = path .res diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 5d4e67d1a0e6..e4a6a0fedc5a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -177,8 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) | hir::Node::Pat(&hir::Pat { kind: - hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) - | hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) + hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) | hir::PatKind::TupleStruct(QPath::TypeRelative(rcvr, segment), ..), span, .. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index cbd1db2ca255..0aa2f1110fb3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -11,8 +11,8 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{ - self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind, - expr_needs_parens, + self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr, + PatExprKind, PatKind, expr_needs_parens, }; use rustc_infer::infer; use rustc_middle::traits::PatternOriginExpr; @@ -312,9 +312,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'_, 'tcx>) { let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; - let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) + let path_res = match pat.kind { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span)) } _ => None, }; @@ -333,6 +333,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + let ty = self.check_pat_path( + *hir_id, + pat.hir_id, + *span, + qpath, + path_res.unwrap(), + expected, + ti, + ); + self.write_ty(*hir_id, ty); + ty + } PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Binding(ba, var_id, ident, sub) => { @@ -341,9 +354,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) } - PatKind::Path(ref qpath) => { - self.check_pat_path(pat.hir_id, pat.span, qpath, path_res.unwrap(), expected, ti) - } PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } @@ -456,16 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Slice(..) => AdjustMode::Peel, // A never pattern behaves somewhat like a literal or unit variant. PatKind::Never => AdjustMode::Peel, - // String and byte-string literals result in types `&str` and `&[u8]` respectively. - // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, - PatKind::Path(_) => match opt_path_res.unwrap() { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() { // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. // Peeling the reference types too early will cause type checking failures. // Although it would be possible to *also* peel the types of the constants too. @@ -476,6 +477,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a reference type wherefore peeling doesn't give up any expressiveness. _ => AdjustMode::Peel, }, + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. + // + // Call `resolve_vars_if_possible` here for inline const blocks. + PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { + ty::Ref(..) => AdjustMode::Pass, + _ => AdjustMode::Peel, + }, + // Ref patterns are complicated, we handle them in `check_pat_ref`. PatKind::Ref(..) => AdjustMode::Pass, // A `_` pattern works with any expected type, so there's no need to do anything. @@ -1001,7 +1013,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Never | PatKind::Binding(..) - | PatKind::Path(..) | PatKind::Box(..) | PatKind::Deref(_) | PatKind::Ref(..) @@ -1139,7 +1150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_path( &self, - hir_id: HirId, + path_id: HirId, + pat_id_for_diag: HirId, span: Span, qpath: &hir::QPath<'_>, path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), @@ -1193,11 +1205,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the path. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, span, span, hir_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); if let Err(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) { - self.emit_bad_pat_path(err, hir_id, span, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments); } pat_ty } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 166ff60f7e1e..546df4497adf 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -6,7 +6,7 @@ use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{ AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, - PatKind, Path, PathSegment, QPath, Ty, TyKind, + PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind, }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -164,11 +164,9 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let span = match cx.tcx.parent_hir_node(ty.hir_id) { - Node::Pat(Pat { - kind: - PatKind::Path(qpath) - | PatKind::TupleStruct(qpath, ..) - | PatKind::Struct(qpath, ..), + Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | Node::Pat(Pat { + kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), .. }) | Node::Expr( diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index e09049f322fa..0c180ab95706 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,7 +1,7 @@ use rustc_abi::ExternAbi; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatExprKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; @@ -527,7 +527,11 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { // Lint for constants that look like binding identifiers (#7526) - if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind { + if let PatKind::Expr(hir::PatExpr { + kind: PatExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = p.kind + { if let Res::Def(DefKind::Const, _) = path.res { if let [segment] = path.segments { NonUpperCaseGlobals::check_upper_case( diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 44b038bb5faf..20a728d6d5b2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -332,10 +332,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .unwrap_or_else(PatKind::Error) } - hir::PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.span); - } - hir::PatKind::Deref(subpattern) => { let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e5b63b9b4a60..bc2bebdfef68 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -10,11 +10,10 @@ use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatExpr, PatExprKind, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -637,8 +636,8 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let res = self.typeck_results().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } - PatKind::Path(ref qpath) => { - let res = self.typeck_results().qpath_res(qpath, pat.hir_id); + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, .. }) => { + let res = self.typeck_results().qpath_res(qpath, *hir_id); self.handle_res(res); } PatKind::TupleStruct(ref qpath, fields, dotdot) => { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 8b10543f6fdb..75b62a40ff9f 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -298,7 +298,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TupleStruct, Or, Never, - Path, Tuple, Box, Deref, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 80dc6b7250cc..77040aeb94d7 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -303,7 +303,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { return kw::Underscore; } PatKind::Binding(_, _, ident, _) => return ident.name, - PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), + PatKind::TupleStruct(ref p, ..) + | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p), PatKind::Or(pats) => { pats.iter().map(|p| name_from_pat(p).to_string()).collect::>().join(" | ") } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 85f87f01afdf..a15ac155123d 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -4,7 +4,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatKind, QPath}; +use rustc_hir::{ + ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, +}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -191,17 +193,21 @@ impl SpanMapVisitor<'_> { } fn handle_pat(&mut self, p: &Pat<'_>) { + let mut check_qpath = |qpath, hir_id| match qpath { + QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { + self.infer_id(path.hir_id, Some(hir_id), qpath.span()); + } + QPath::Resolved(_, path) => self.handle_path(path), + _ => {} + }; match p.kind { PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), - PatKind::Struct(qpath, _, _) - | PatKind::TupleStruct(qpath, _, _) - | PatKind::Path(qpath) => match qpath { - QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { - self.infer_id(path.hir_id, Some(p.hir_id), qpath.span()); - } - QPath::Resolved(_, path) => self.handle_path(path), - _ => {} - }, + PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => { + check_qpath(qpath, p.hir_id) + } + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => { + check_qpath(*qpath, *hir_id) + } PatKind::Or(pats) => { for pat in pats { self.handle_pat(pat); diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 8a5cf7f56d5f..7ca2c9536998 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), - PatKind::Path(_) | PatKind::Expr(_) => true, + PatKind::Expr(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 8503dde3fb6b..274785061b3f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -292,7 +292,12 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo // Only do the check if the type is "spelled out" in the pattern if !matches!( pat.kind, - PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Expr(PatExpr { + kind: PatExprKind::Path(..), + .. + },) ) { return; } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 8f8390b6f3f9..7b95399c907c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::GenericArgKind; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { } fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let PatKind::Path(QPath::Resolved(_, path)) = arm.pat.kind + if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match // against it. diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 99a7b8c74be8..97e8423695d9 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, HirId, Pat, PatKind}; +use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -119,7 +119,11 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index b1a555b91d1b..3deaaf96c1e8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -6,7 +6,7 @@ use rustc_ast::BindingMode; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::symbol::Ident; @@ -60,7 +60,16 @@ pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Exp /// accepted. fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool { match pat.kind { - PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true, + PatKind::Wild + | PatKind::Expr(PatExpr { + kind: PatExprKind::Path(_), + .. + }) + | PatKind::Binding(_, _, _, None) + if can_be_wild => + { + true + }, PatKind::TupleStruct(qpath, ..) => { is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err }, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 59d375200117..b69294d567d1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -7,7 +7,7 @@ use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, Pat, PatKind}; +use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::sym; @@ -89,7 +89,11 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(& if arms.len() == 2 && arms.iter().all(|arm| arm.guard.is_none()) && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + hir_id, + kind: PatExprKind::Path(qpath), + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [pat], _) => { matches!(pat.kind, PatKind::Wild) && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 0b57740064c1..2ad55d9bf1fd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def::Res; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::{SyntaxContext, sym}; @@ -256,9 +256,11 @@ pub(super) fn try_parse_pattern<'tcx>( match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), PatKind::TupleStruct(ref qpath, [pattern], _) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 6c123649afc2..b1889d26c932 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -59,7 +59,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { matches!( arm.pat.kind, - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) ) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 28e05c273d5c..41e4c75f843e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -292,7 +292,11 @@ impl<'a> NormalizedPat<'a> { Self::Tuple(var_id, pats) }, PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))), - PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path), + hir_id, + .. + }) => Self::Path(cx.qpath_res(path, *hir_id).opt_def_id()), PatKind::Tuple(pats, wild_idx) => { let field_count = match cx.typeck_results().pat_ty(pat).kind() { ty::Tuple(subs) => subs.len(), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 595655600890..11b588b33554 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; +use rustc_hir::{Arm, Expr, PatExpr, PatExprKind, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; use rustc_span::sym; @@ -60,8 +60,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // covered by the set of guards that cover it, but that's really hard to do. recurse_or_patterns(arm.pat, |pat| { let path = match &peel_hir_pat_refs(pat).0.kind { - PatKind::Path(path) => { - let id = match cx.qpath_res(path, pat.hir_id) { + PatKind::Expr(PatExpr { + hir_id, + kind: PatExprKind::Path(path), + .. + }) => { + // FIXME(clippy): don't you want to use the hir id of the peeled pat? + let id = match cx.qpath_res(path, *hir_id) { Res::Def( DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, _, diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 0d5575efc220..7e65d586110e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; +use rustc_hir::{ + Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, +}; use rustc_lint::LateContext; use rustc_span::sym; @@ -183,7 +185,13 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` - (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { + ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(QPath::Resolved(_, p_path)), + .. + }), + ExprKind::Path(QPath::Resolved(_, e_path)), + ) => { return over(p_path.segments, e_path.segments, |p_seg, e_seg| { p_seg.ident.name == e_seg.ident.name }); diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index edac97344a03..393399660131 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -149,8 +149,12 @@ fn find_method_and_type<'tcx>( None } }, - PatKind::Path(ref path) => { - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path), + hir_id, + .. + }) => { + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, *hir_id) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) { let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { @@ -351,10 +355,20 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right)) - | (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _)) - if patterns.len() == 1 => - { + ( + PatKind::TupleStruct(path_left, patterns, _), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_right), + .. + }), + ) + | ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_left), + .. + }), + PatKind::TupleStruct(path_right, patterns, _), + ) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { find_good_method_for_match( cx, @@ -389,7 +403,13 @@ fn found_good_method<'tcx>( None } }, - (PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_left), + .. + }), + PatKind::Wild, + ) => get_good_method(cx, arms, path_left), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 38f876fed802..2f46eaaabb36 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind + let (msg, sugg) = if let PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -331,14 +331,16 @@ impl<'a> PatState<'a> { #[expect(clippy::similar_names)] fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { match pat.kind { - PatKind::Path(_) - if match *cx.typeck.pat_ty(pat).peel_refs().kind() { - ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), - ty::Tuple(tys) => !tys.is_empty(), - ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1), - ty::Slice(..) => true, - _ => false, - } => + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(_), + .. + }) if match *cx.typeck.pat_ty(pat).peel_refs().kind() { + ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), + ty::Tuple(tys) => !tys.is_empty(), + ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1), + ty::Slice(..) => true, + _ => false, + } => { matches!(self, Self::Wild) }, @@ -386,7 +388,6 @@ impl<'a> PatState<'a> { | PatKind::Binding(_, _, _, None) | PatKind::Expr(_) | PatKind::Range(..) - | PatKind::Path(_) | PatKind::Never | PatKind::Err(_) => { *self = PatState::Wild; diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 6d9e75f51d66..de9f055863cb 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -7,7 +7,9 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; -use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp}; +use rustc_hir::{ + Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::SyntaxContext; @@ -281,7 +283,11 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) && matches!(first_pat.kind, PatKind::Wild) diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 84b6430294f3..47ce2243aa09 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -258,7 +258,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() // get the path from the pattern - && let PatKind::Path(QPath::Resolved(_, path)) + && let PatKind::Expr(&PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind && cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity() diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 4dcc8ac7fb0a..6bad78cf8718 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -708,11 +708,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.qpath(qpath); self.slice(fields, |pat| self.pat(pat)); }, - PatKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, PatKind::Tuple(fields, skip_pos) => { bind!(self, fields); kind!("Tuple({fields}, {skip_pos:?})"); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index d76231a6eeaa..d0eb5318e645 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -524,7 +524,6 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -1120,7 +1119,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); } }, - PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { self.hash_pat_expr(s); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index e471dfb6ef19..5a5227af9074 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -106,8 +106,8 @@ use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, - PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, - TyKind, UnOp, def, + PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, + TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -560,7 +560,20 @@ macro_rules! maybe_path { }; } maybe_path!(Expr, ExprKind); -maybe_path!(Pat, PatKind); +impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn hir_id(&self) -> HirId { + self.hir_id + } + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match &self.kind { + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + .. + }) => Some(qpath), + _ => None, + } + } +} maybe_path!(Ty, TyKind); /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` @@ -1753,7 +1766,11 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), - PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_enum_variant(cx, qpath, *hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set are_refutable(cx, pats) diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 916f296ccfc6..910582ae4d9e 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -26,16 +26,16 @@ params: [ body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Scope { - region_scope: Node(26) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + region_scope: Node(28) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).28)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Block { @@ -47,7 +47,7 @@ body: expr: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Scope { @@ -56,14 +56,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Match { scrutinee: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: Scope { @@ -72,7 +72,7 @@ body: value: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: VarRef { @@ -123,16 +123,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { - region_scope: Node(14) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + region_scope: Node(15) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).15)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -140,8 +140,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).13)) - scope: Node(13) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + scope: Node(14) span: $DIR/thir-tree-match.rs:17:9: 17:40 (#0) } Arm { @@ -175,16 +175,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { - region_scope: Node(20) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + region_scope: Node(21) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).21)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -192,8 +192,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).19)) - scope: Node(19) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + scope: Node(20) span: $DIR/thir-tree-match.rs:18:9: 18:32 (#0) } Arm { @@ -219,16 +219,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { - region_scope: Node(25) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).25)) + region_scope: Node(27) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).27)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) @@ -236,8 +236,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).24)) - scope: Node(24) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + scope: Node(26) span: $DIR/thir-tree-match.rs:19:9: 19:28 (#0) } ] From 8f09abb49757a65f143c6794fcb22a70945e67d1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 29 Jan 2025 08:38:56 +0000 Subject: [PATCH 252/342] Add regression test showing we don't realize some consts are used --- tests/ui/lint/dead-code/lint-dead-code-1.rs | 2 ++ tests/ui/lint/dead-code/lint-dead-code-1.stderr | 14 +++++++------- tests/ui/pattern/issue-110508.rs | 8 +++++++- tests/ui/pattern/issue-110508.stderr | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 tests/ui/pattern/issue-110508.stderr diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index ddcafedf7bc5..a7f654b5d8bf 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -29,6 +29,7 @@ const used_const: isize = 0; pub const used_const2: isize = used_const; const USED_CONST: isize = 1; const CONST_USED_IN_ENUM_DISCRIMINANT: isize = 11; +const CONST_USED_IN_RANGE_PATTERN: isize = 12; pub type typ = *const UsedStruct4; pub struct PubStruct; @@ -81,6 +82,7 @@ pub fn pub_fn() { match i { USED_STATIC => (), USED_CONST => (), + CONST_USED_IN_RANGE_PATTERN..100 => {} _ => () } f::(); diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index eb728b5b9305..c4410114cea8 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -17,19 +17,19 @@ LL | const priv_const: isize = 0; | ^^^^^^^^^^ error: struct `PrivStruct` is never constructed - --> $DIR/lint-dead-code-1.rs:35:8 + --> $DIR/lint-dead-code-1.rs:36:8 | LL | struct PrivStruct; | ^^^^^^^^^^ error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:64:6 + --> $DIR/lint-dead-code-1.rs:65:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:67:5 + --> $DIR/lint-dead-code-1.rs:68:5 | LL | enum used_enum { | --------- variant in this enum @@ -38,25 +38,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:88:4 + --> $DIR/lint-dead-code-1.rs:90:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:93:4 + --> $DIR/lint-dead-code-1.rs:95:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:98:4 + --> $DIR/lint-dead-code-1.rs:100:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:102:4 + --> $DIR/lint-dead-code-1.rs:104:4 | LL | fn baz() -> impl Copy { | ^^^ diff --git a/tests/ui/pattern/issue-110508.rs b/tests/ui/pattern/issue-110508.rs index 6ed0476183ec..980d8d52f1d5 100644 --- a/tests/ui/pattern/issue-110508.rs +++ b/tests/ui/pattern/issue-110508.rs @@ -1,4 +1,4 @@ -//@ run-pass +#![deny(dead_code)] #[derive(PartialEq, Eq)] pub enum Foo { @@ -11,6 +11,7 @@ impl Foo { const A2: Foo = Self::FooA(()); const A3: Self = Foo::FooA(()); const A4: Self = Self::FooA(()); + const A5: u32 = 1; //~ ERROR: dead_code } fn main() { @@ -35,4 +36,9 @@ fn main() { Foo::A4 => {}, _ => {}, } + + match 3 { + Foo::A5..5 => {} + _ => {} + } } diff --git a/tests/ui/pattern/issue-110508.stderr b/tests/ui/pattern/issue-110508.stderr new file mode 100644 index 000000000000..aaec91167692 --- /dev/null +++ b/tests/ui/pattern/issue-110508.stderr @@ -0,0 +1,17 @@ +error: associated constant `A5` is never used + --> $DIR/issue-110508.rs:14:11 + | +LL | impl Foo { + | -------- associated constant in this implementation +... +LL | const A5: u32 = 1; + | ^^ + | +note: the lint level is defined here + --> $DIR/issue-110508.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 559648a0a4c942993c321d999f708e64f169b245 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 29 Jan 2025 08:52:43 +0000 Subject: [PATCH 253/342] Handle all `PatExpr`s in dead code analysis --- compiler/rustc_passes/src/dead.rs | 17 ++++++++++++----- tests/ui/pattern/issue-110508.rs | 4 +++- tests/ui/pattern/issue-110508.stderr | 17 ----------------- 3 files changed, 15 insertions(+), 23 deletions(-) delete mode 100644 tests/ui/pattern/issue-110508.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index bc2bebdfef68..95f18eaa7ef1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,7 +13,7 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatExpr, PatExprKind, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -636,10 +636,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let res = self.typeck_results().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } - PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, .. }) => { - let res = self.typeck_results().qpath_res(qpath, *hir_id); - self.handle_res(res); - } PatKind::TupleStruct(ref qpath, fields, dotdot) => { let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_tuple_field_pattern_match(pat, res, fields, dotdot); @@ -651,6 +647,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = false; } + fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) { + match &expr.kind { + rustc_hir::PatExprKind::Path(qpath) => { + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + self.handle_res(res); + } + _ => {} + } + intravisit::walk_pat_expr(self, expr); + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { self.handle_res(path.res); intravisit::walk_path(self, path); diff --git a/tests/ui/pattern/issue-110508.rs b/tests/ui/pattern/issue-110508.rs index 980d8d52f1d5..74a8d673e837 100644 --- a/tests/ui/pattern/issue-110508.rs +++ b/tests/ui/pattern/issue-110508.rs @@ -1,3 +1,5 @@ +//@ run-pass + #![deny(dead_code)] #[derive(PartialEq, Eq)] @@ -11,7 +13,7 @@ impl Foo { const A2: Foo = Self::FooA(()); const A3: Self = Foo::FooA(()); const A4: Self = Self::FooA(()); - const A5: u32 = 1; //~ ERROR: dead_code + const A5: u32 = 1; } fn main() { diff --git a/tests/ui/pattern/issue-110508.stderr b/tests/ui/pattern/issue-110508.stderr deleted file mode 100644 index aaec91167692..000000000000 --- a/tests/ui/pattern/issue-110508.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: associated constant `A5` is never used - --> $DIR/issue-110508.rs:14:11 - | -LL | impl Foo { - | -------- associated constant in this implementation -... -LL | const A5: u32 = 1; - | ^^ - | -note: the lint level is defined here - --> $DIR/issue-110508.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - From 2f276b36e203c05b3c38b427d6e38219ae6a2cf1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 29 Jan 2025 13:27:41 -0300 Subject: [PATCH 254/342] spastorino back from vacations --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 4a09fe116a57..6beaae2b9b74 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1023,7 +1023,6 @@ contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "jyn514", "nnethercote", - "spastorino", "workingjubilee", "kobzol" ] From e4db6d7825cc22991f2c73623c39c457e86a1600 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 27 Jan 2025 13:55:21 -0800 Subject: [PATCH 255/342] Add release notes for 1.84.1 --- RELEASES.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 2da6ed3f1006..e0175db7ec88 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,18 @@ +Version 1.84.1 (2025-01-30) +========================== + + + +- [Fix ICE 132920 in duplicate-crate diagnostics.](https://github.com/rust-lang/rust/pull/133304/) +- [Fix errors for overlapping impls in incremental rebuilds.](https://github.com/rust-lang/rust/pull/133828/) +- [Fix slow compilation related to the next-generation trait solver.](https://github.com/rust-lang/rust/pull/135618/) +- [Fix debuginfo when LLVM's location discriminator value limit is exceeded.](https://github.com/rust-lang/rust/pull/135643/) +- Fixes for building Rust from source: + - [Only try to distribute `llvm-objcopy` if llvm tools are enabled.](https://github.com/rust-lang/rust/pull/134240/) + - [Add Profile Override for Non-Git Sources.](https://github.com/rust-lang/rust/pull/135433/) + - [Resolve symlinks of LLVM tool binaries before copying them.](https://github.com/rust-lang/rust/pull/135585/) + - [Make it possible to use ci-rustc on tarball sources.](https://github.com/rust-lang/rust/pull/135722/) + Version 1.84.0 (2025-01-09) ========================== From fdef34b4ddfeebf614bb58622b4053cff616234a Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 11:20:22 -0700 Subject: [PATCH 256/342] Add URL and `crate_name` to test cases --- tests/rustdoc/inline_cross/issue-24183.rs | 1 + tests/rustdoc/inline_cross/issue-28480.rs | 5 ++++- tests/rustdoc/inline_cross/issue-31948-1.rs | 13 ++++++++----- tests/rustdoc/inline_cross/issue-31948-2.rs | 13 ++++++++----- tests/rustdoc/inline_cross/issue-31948.rs | 17 ++++++++++------- tests/rustdoc/inline_cross/issue-32881.rs | 5 ++++- tests/rustdoc/inline_cross/issue-33113.rs | 5 ++++- tests/rustdoc/inline_cross/issue-76736-1.rs | 2 ++ tests/rustdoc/inline_cross/issue-76736-2.rs | 2 ++ tests/rustdoc/inline_cross/issue-76736-3.rs | 2 ++ tests/rustdoc/inline_cross/issue-76736-4.rs | 2 ++ tests/rustdoc/inline_local/issue-28537.rs | 7 +++++-- tests/rustdoc/inline_local/issue-32343.rs | 13 ++++++++----- tests/rustdoc/intra-doc/issue-103463.rs | 2 ++ tests/rustdoc/intra-doc/issue-104145.rs | 2 ++ tests/rustdoc/intra-doc/issue-108459.rs | 11 +++++++---- tests/rustdoc/intra-doc/issue-66159.rs | 5 ++++- tests/rustdoc/intra-doc/issue-82209.rs | 2 ++ 18 files changed, 77 insertions(+), 32 deletions(-) diff --git a/tests/rustdoc/inline_cross/issue-24183.rs b/tests/rustdoc/inline_cross/issue-24183.rs index 8299eecc575d..909005532f50 100644 --- a/tests/rustdoc/inline_cross/issue-24183.rs +++ b/tests/rustdoc/inline_cross/issue-24183.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/24183 #![crate_type = "lib"] #![crate_name = "usr"] diff --git a/tests/rustdoc/inline_cross/issue-28480.rs b/tests/rustdoc/inline_cross/issue-28480.rs index 004510fd9225..e1ca7403c03f 100644 --- a/tests/rustdoc/inline_cross/issue-28480.rs +++ b/tests/rustdoc/inline_cross/issue-28480.rs @@ -1,3 +1,6 @@ +// https://github.com/rust-lang/rust/issues/28480 +#![crate_name="foobar"] + //@ aux-build:rustdoc-hidden-sig.rs //@ build-aux-docs //@ ignore-cross-compile @@ -7,7 +10,7 @@ //@ has - '//a' 'u8' extern crate rustdoc_hidden_sig; -//@ has issue_28480/struct.Bar.html +//@ has foobar/struct.Bar.html //@ !has - '//a/@title' 'Hidden' //@ has - '//a' 'u8' pub use rustdoc_hidden_sig::Bar; diff --git a/tests/rustdoc/inline_cross/issue-31948-1.rs b/tests/rustdoc/inline_cross/issue-31948-1.rs index e59da87c29de..baab1da95470 100644 --- a/tests/rustdoc/inline_cross/issue-31948-1.rs +++ b/tests/rustdoc/inline_cross/issue-31948-1.rs @@ -1,27 +1,30 @@ +// https://github.com/rust-lang/rust/issues/31948 +#![crate_name="foobar"] + //@ aux-build:rustdoc-nonreachable-impls.rs //@ build-aux-docs //@ ignore-cross-compile extern crate rustdoc_nonreachable_impls; -//@ has issue_31948_1/struct.Wobble.html +//@ has foobar/struct.Wobble.html //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for' //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for' //@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for' //@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for' pub use rustdoc_nonreachable_impls::hidden::Wobble; -//@ has issue_31948_1/trait.Bark.html +//@ has foobar/trait.Bark.html //@ has - '//h3[@class="code-header"]' 'for Foo' //@ has - '//h3[@class="code-header"]' 'for Wobble' //@ !has - '//h3[@class="code-header"]' 'for Wibble' pub use rustdoc_nonreachable_impls::Bark; -//@ has issue_31948_1/trait.Woof.html +//@ has foobar/trait.Woof.html //@ has - '//h3[@class="code-header"]' 'for Foo' //@ has - '//h3[@class="code-header"]' 'for Wobble' //@ !has - '//h3[@class="code-header"]' 'for Wibble' pub use rustdoc_nonreachable_impls::Woof; -//@ !has issue_31948_1/trait.Bar.html -//@ !has issue_31948_1/trait.Qux.html +//@ !has foobar/trait.Bar.html +//@ !has foobar/trait.Qux.html diff --git a/tests/rustdoc/inline_cross/issue-31948-2.rs b/tests/rustdoc/inline_cross/issue-31948-2.rs index 34b570528832..40e9108ec62e 100644 --- a/tests/rustdoc/inline_cross/issue-31948-2.rs +++ b/tests/rustdoc/inline_cross/issue-31948-2.rs @@ -1,21 +1,24 @@ +// https://github.com/rust-lang/rust/issues/31948 +#![crate_name="foobar"] + //@ aux-build:rustdoc-nonreachable-impls.rs //@ build-aux-docs //@ ignore-cross-compile extern crate rustdoc_nonreachable_impls; -//@ has issue_31948_2/struct.Wobble.html +//@ has foobar/struct.Wobble.html //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for' //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for' //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for' //@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for' pub use rustdoc_nonreachable_impls::hidden::Wobble; -//@ has issue_31948_2/trait.Qux.html +//@ has foobar/trait.Qux.html //@ has - '//h3[@class="code-header"]' 'for Foo' //@ has - '//h3[@class="code-header"]' 'for Wobble' pub use rustdoc_nonreachable_impls::hidden::Qux; -//@ !has issue_31948_2/trait.Bar.html -//@ !has issue_31948_2/trait.Woof.html -//@ !has issue_31948_2/trait.Bark.html +//@ !has foobar/trait.Bar.html +//@ !has foobar/trait.Woof.html +//@ !has foobar/trait.Bark.html diff --git a/tests/rustdoc/inline_cross/issue-31948.rs b/tests/rustdoc/inline_cross/issue-31948.rs index 7a43fc7b279d..ab0048513c72 100644 --- a/tests/rustdoc/inline_cross/issue-31948.rs +++ b/tests/rustdoc/inline_cross/issue-31948.rs @@ -1,29 +1,32 @@ +// https://github.com/rust-lang/rust/issues/31948 +#![crate_name="foobar"] + //@ aux-build:rustdoc-nonreachable-impls.rs //@ build-aux-docs //@ ignore-cross-compile extern crate rustdoc_nonreachable_impls; -//@ has issue_31948/struct.Foo.html +//@ has foobar/struct.Foo.html //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for' //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for' //@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for' //@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for' pub use rustdoc_nonreachable_impls::Foo; -//@ has issue_31948/trait.Bark.html +//@ has foobar/trait.Bark.html //@ has - '//h3[@class="code-header"]' 'for Foo' //@ !has - '//h3[@class="code-header"]' 'for Wibble' //@ !has - '//h3[@class="code-header"]' 'for Wobble' pub use rustdoc_nonreachable_impls::Bark; -//@ has issue_31948/trait.Woof.html +//@ has foobar/trait.Woof.html //@ has - '//h3[@class="code-header"]' 'for Foo' //@ !has - '//h3[@class="code-header"]' 'for Wibble' //@ !has - '//h3[@class="code-header"]' 'for Wobble' pub use rustdoc_nonreachable_impls::Woof; -//@ !has issue_31948/trait.Bar.html -//@ !has issue_31948/trait.Qux.html -//@ !has issue_31948/struct.Wibble.html -//@ !has issue_31948/struct.Wobble.html +//@ !has foobar/trait.Bar.html +//@ !has foobar/trait.Qux.html +//@ !has foobar/struct.Wibble.html +//@ !has foobar/struct.Wobble.html diff --git a/tests/rustdoc/inline_cross/issue-32881.rs b/tests/rustdoc/inline_cross/issue-32881.rs index d4ebf10a1ca9..f7dc74144552 100644 --- a/tests/rustdoc/inline_cross/issue-32881.rs +++ b/tests/rustdoc/inline_cross/issue-32881.rs @@ -1,10 +1,13 @@ +// https://github.com/rust-lang/rust/issues/32881 +#![crate_name="foobar"] + //@ aux-build:rustdoc-trait-object-impl.rs //@ build-aux-docs //@ ignore-cross-compile extern crate rustdoc_trait_object_impl; -//@ has issue_32881/trait.Bar.html +//@ has foobar/trait.Bar.html //@ has - '//h3[@class="code-header"]' "impl<'a> dyn Bar" //@ has - '//h3[@class="code-header"]' "impl<'a> Debug for dyn Bar" diff --git a/tests/rustdoc/inline_cross/issue-33113.rs b/tests/rustdoc/inline_cross/issue-33113.rs index 05e87d962cb6..9ac4f02e00c1 100644 --- a/tests/rustdoc/inline_cross/issue-33113.rs +++ b/tests/rustdoc/inline_cross/issue-33113.rs @@ -1,10 +1,13 @@ +// https://github.com/rust-lang/rust/issues/33113 +#![crate_name="foobar"] + //@ aux-build:issue-33113.rs //@ build-aux-docs //@ ignore-cross-compile extern crate bar; -//@ has issue_33113/trait.Bar.html +//@ has foobar/trait.Bar.html //@ has - '//h3[@class="code-header"]' "for &'a char" //@ has - '//h3[@class="code-header"]' "for Foo" pub use bar::Bar; diff --git a/tests/rustdoc/inline_cross/issue-76736-1.rs b/tests/rustdoc/inline_cross/issue-76736-1.rs index fe52702fd6f5..3ffa5e6cc06e 100644 --- a/tests/rustdoc/inline_cross/issue-76736-1.rs +++ b/tests/rustdoc/inline_cross/issue-76736-1.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/76736 + //@ aux-build:issue-76736-1.rs //@ aux-build:issue-76736-2.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-2.rs b/tests/rustdoc/inline_cross/issue-76736-2.rs index df376ebe9a14..843b2941602c 100644 --- a/tests/rustdoc/inline_cross/issue-76736-2.rs +++ b/tests/rustdoc/inline_cross/issue-76736-2.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/76736 + //@ aux-build:issue-76736-1.rs //@ aux-build:issue-76736-2.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-3.rs b/tests/rustdoc/inline_cross/issue-76736-3.rs index 1bed4621c049..f9b46caa02ff 100644 --- a/tests/rustdoc/inline_cross/issue-76736-3.rs +++ b/tests/rustdoc/inline_cross/issue-76736-3.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/76736 + //@ compile-flags: -Zforce-unstable-if-unmarked //@ aux-build:issue-76736-1.rs //@ aux-build:issue-76736-2.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-4.rs b/tests/rustdoc/inline_cross/issue-76736-4.rs index 487e90301082..511464f2c498 100644 --- a/tests/rustdoc/inline_cross/issue-76736-4.rs +++ b/tests/rustdoc/inline_cross/issue-76736-4.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/76736 + //@ aux-build:issue-76736-1.rs //@ aux-build:issue-76736-2.rs diff --git a/tests/rustdoc/inline_local/issue-28537.rs b/tests/rustdoc/inline_local/issue-28537.rs index d5ba94d2e6c9..0e9836c7ceea 100644 --- a/tests/rustdoc/inline_local/issue-28537.rs +++ b/tests/rustdoc/inline_local/issue-28537.rs @@ -1,3 +1,6 @@ +// https://github.com/rust-lang/rust/issues/28537 +#![crate_name="foo"] + #[doc(hidden)] pub mod foo { pub struct Foo; @@ -10,8 +13,8 @@ mod bar { } } -//@ has issue_28537/struct.Foo.html +//@ has foo/struct.Foo.html pub use foo::Foo; -//@ has issue_28537/struct.Bar.html +//@ has foo/struct.Bar.html pub use self::bar::Bar; diff --git a/tests/rustdoc/inline_local/issue-32343.rs b/tests/rustdoc/inline_local/issue-32343.rs index 2ec123fdc5cf..ed11614a5003 100644 --- a/tests/rustdoc/inline_local/issue-32343.rs +++ b/tests/rustdoc/inline_local/issue-32343.rs @@ -1,12 +1,15 @@ -//@ !has issue_32343/struct.Foo.html -//@ has issue_32343/index.html +// https://github.com/rust-lang/rust/issues/32343 +#![crate_name="foobar"] + +//@ !has foobar/struct.Foo.html +//@ has foobar/index.html //@ has - '//code' 'pub use foo::Foo' //@ !has - '//code/a' 'Foo' #[doc(no_inline)] pub use foo::Foo; -//@ !has issue_32343/struct.Bar.html -//@ has issue_32343/index.html +//@ !has foobar/struct.Bar.html +//@ has foobar/index.html //@ has - '//code' 'pub use foo::Bar' //@ has - '//code/a' 'Bar' #[doc(no_inline)] @@ -18,6 +21,6 @@ mod foo { } pub mod bar { - //@ has issue_32343/bar/struct.Bar.html + //@ has foobar/bar/struct.Bar.html pub use ::foo::Bar; } diff --git a/tests/rustdoc/intra-doc/issue-103463.rs b/tests/rustdoc/intra-doc/issue-103463.rs index 9b5cb67fd32a..bfe5c4f05d4d 100644 --- a/tests/rustdoc/intra-doc/issue-103463.rs +++ b/tests/rustdoc/intra-doc/issue-103463.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/103463 + // The `Trait` is not pulled into the crate resulting in doc links in its methods being resolved. //@ aux-build:issue-103463-aux.rs diff --git a/tests/rustdoc/intra-doc/issue-104145.rs b/tests/rustdoc/intra-doc/issue-104145.rs index 5690803af5ae..92c7fa31b4fe 100644 --- a/tests/rustdoc/intra-doc/issue-104145.rs +++ b/tests/rustdoc/intra-doc/issue-104145.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/104145 + // Doc links in `Trait`'s methods are resolved because it has a local impl. //@ aux-build:issue-103463-aux.rs diff --git a/tests/rustdoc/intra-doc/issue-108459.rs b/tests/rustdoc/intra-doc/issue-108459.rs index 18424c069d35..0328e6435a53 100644 --- a/tests/rustdoc/intra-doc/issue-108459.rs +++ b/tests/rustdoc/intra-doc/issue-108459.rs @@ -1,3 +1,6 @@ +// https://github.com/rust-lang/rust/issues/108459 +#![crate_name="foobar"] + #![deny(rustdoc::broken_intra_doc_links)] #![allow(rustdoc::redundant_explicit_links)] @@ -13,13 +16,13 @@ pub struct MyStruct1; // the same target but different text /// See also [crate::char] and [mod@char] and [prim@char] -//@ has issue_108459/struct.MyStruct2.html '//*[@href="char/index.html"]' 'crate::char' +//@ has foobar/struct.MyStruct2.html '//*[@href="char/index.html"]' 'crate::char' //@ has - '//*[@href="char/index.html"]' 'char' //@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char' pub struct MyStruct2; /// See also [mod@char] and [prim@char] and [crate::char] -//@ has issue_108459/struct.MyStruct3.html '//*[@href="char/index.html"]' 'crate::char' +//@ has foobar/struct.MyStruct3.html '//*[@href="char/index.html"]' 'crate::char' //@ has - '//*[@href="char/index.html"]' 'char' //@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char' pub struct MyStruct3; @@ -28,11 +31,11 @@ pub struct MyStruct3; // different targets /// See also [char][mod@char] and [char][prim@char] -//@ has issue_108459/struct.MyStruct4.html '//*[@href="char/index.html"]' 'char' +//@ has foobar/struct.MyStruct4.html '//*[@href="char/index.html"]' 'char' //@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char' pub struct MyStruct4; /// See also [char][prim@char] and [char][crate::char] -//@ has issue_108459/struct.MyStruct5.html '//*[@href="char/index.html"]' 'char' +//@ has foobar/struct.MyStruct5.html '//*[@href="char/index.html"]' 'char' //@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char' pub struct MyStruct5; diff --git a/tests/rustdoc/intra-doc/issue-66159.rs b/tests/rustdoc/intra-doc/issue-66159.rs index 5d50f63f299f..7e3ace9355ae 100644 --- a/tests/rustdoc/intra-doc/issue-66159.rs +++ b/tests/rustdoc/intra-doc/issue-66159.rs @@ -1,3 +1,6 @@ +// https://github.com/rust-lang/rust/issues/66159 +#![crate_name="foobar"] + //@ aux-crate:priv:pub_struct=pub-struct.rs //@ compile-flags:-Z unstable-options @@ -6,5 +9,5 @@ // Since we don't generate the docs for the auxiliary files, we can't actually // verify that the struct is linked correctly. -//@ has issue_66159/index.html +//@ has foobar/index.html //! [pub_struct::SomeStruct] diff --git a/tests/rustdoc/intra-doc/issue-82209.rs b/tests/rustdoc/intra-doc/issue-82209.rs index 46d028e535c2..615115a5f8b2 100644 --- a/tests/rustdoc/intra-doc/issue-82209.rs +++ b/tests/rustdoc/intra-doc/issue-82209.rs @@ -1,3 +1,5 @@ +// https://github.com/rust-lang/rust/issues/82209 + #![crate_name = "foo"] #![deny(rustdoc::broken_intra_doc_links)] pub enum Foo { From 311c3b71f0972e144cac91679a08906875c8af3f Mon Sep 17 00:00:00 2001 From: edwloef Date: Mon, 27 Jan 2025 16:35:15 +0100 Subject: [PATCH 257/342] split slice::ptr_rotate into three separate algorithms, to hopefully help inlining --- library/core/src/slice/rotate.rs | 373 ++++++++++++++++--------------- 1 file changed, 197 insertions(+), 176 deletions(-) diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index 20833dc31aa2..3e88978b7810 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -1,6 +1,8 @@ use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::{cmp, ptr}; +type BufType = [usize; 32]; + /// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first /// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the /// right. @@ -8,17 +10,76 @@ use crate::{cmp, ptr}; /// # Safety /// /// The specified range must be valid for reading and writing. -/// -/// # Algorithm -/// +pub(super) unsafe fn ptr_rotate(left: usize, mid: *mut T, right: usize) { + if T::IS_ZST { + return; + } + // abort early if the rotate is a no-op + if (left == 0) || (right == 0) { + return; + } + // `T` is not a zero-sized type, so it's okay to divide by its size. + if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_memmove(left, mid, right) }; + } else if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_gcd(left, mid, right) } + } else { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_swap(left, mid, right) } + } +} + /// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The /// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and /// the ones on the buffer are moved back into the hole on the opposite side of where they /// originated. /// -/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough. +/// # Safety /// -/// Algorithm 2 is otherwise used for small values of `left + right` or for large `T`. The elements +/// The specified range must be valid for reading and writing. +unsafe fn ptr_rotate_memmove(left: usize, mid: *mut T, right: usize) { + // The `[T; 0]` here is to ensure this is appropriately aligned for T + let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); + let buf = rawarray.as_mut_ptr() as *mut T; + // SAFETY: `mid-left <= mid-left+right < mid+right` + let dim = unsafe { mid.sub(left).add(right) }; + if left <= right { + // SAFETY: + // + // 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in + // `buf` without overflow and `buf` was created just above and so cannot be + // overlapped with any value of `[mid-left; left]` + // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care + // about overlaps here. + // 3) The `if` condition about `left <= right` ensures writing `left` elements to + // `dim = mid-left+right` is valid because: + // - `buf` is valid and `left` elements were written in it in 1) + // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` + unsafe { + // 1) + ptr::copy_nonoverlapping(mid.sub(left), buf, left); + // 2) + ptr::copy(mid, mid.sub(left), right); + // 3) + ptr::copy_nonoverlapping(buf, dim, left); + } + } else { + // SAFETY: same reasoning as above but with `left` and `right` reversed + unsafe { + ptr::copy_nonoverlapping(mid, buf, right); + ptr::copy(mid.sub(left), dim, left); + ptr::copy_nonoverlapping(buf, mid.sub(left), right); + } + } +} + +/// Algorithm 2 is used for small values of `left + right` or for large `T`. The elements /// are moved into their final positions one at a time starting at `mid - left` and advancing by /// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we /// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps @@ -48,9 +109,101 @@ use crate::{cmp, ptr}; /// /// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too /// few rounds on average until `left + right` is enormous, and the worst case of a single -/// round is always there. Instead, algorithm 3 utilizes repeated swapping of -/// `min(left, right)` elements until a smaller rotate problem is left. +/// round is always there. /// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +unsafe fn ptr_rotate_gcd(left: usize, mid: *mut T, right: usize) { + // Algorithm 2 + // Microbenchmarks indicate that the average performance for random shifts is better all + // the way until about `left + right == 32`, but the worst case performance breaks even + // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 + // `usize`s, this algorithm also outperforms other algorithms. + // SAFETY: callers must ensure `mid - left` is valid for reading and writing. + let x = unsafe { mid.sub(left) }; + // beginning of first round + // SAFETY: see previous comment. + let mut tmp: T = unsafe { x.read() }; + let mut i = right; + // `gcd` can be found before hand by calculating `gcd(left + right, right)`, + // but it is faster to do one loop which calculates the gcd as a side effect, then + // doing the rest of the chunk + let mut gcd = right; + // benchmarks reveal that it is faster to swap temporaries all the way through instead + // of reading one temporary once, copying backwards, and then writing that temporary at + // the very end. This is possibly due to the fact that swapping or replacing temporaries + // uses only one memory address in the loop instead of needing to manage two. + loop { + // [long-safety-expl] + // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and + // writing. + // + // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` + // - `i <= left+right-1` is always true + // - if `i < left`, `right` is added so `i < left+right` and on the next + // iteration `left` is removed from `i` so it doesn't go further + // - if `i >= left`, `left` is removed immediately and so it doesn't go further. + // - overflows cannot happen for `i` since the function's safety contract ask for + // `mid+right-1 = x+left+right` to be valid for writing + // - underflows cannot happen because `i` must be bigger or equal to `left` for + // a subtraction of `left` to happen. + // + // So `x+i` is valid for reading and writing if the caller respected the contract + tmp = unsafe { x.add(i).replace(tmp) }; + // instead of incrementing `i` and then checking if it is outside the bounds, we + // check if `i` will go outside the bounds on the next increment. This prevents + // any wrapping of pointers or `usize`. + if i >= left { + i -= left; + if i == 0 { + // end of first round + // SAFETY: tmp has been read from a valid source and x is valid for writing + // according to the caller. + unsafe { x.write(tmp) }; + break; + } + // this conditional must be here if `left + right >= 15` + if i < gcd { + gcd = i; + } + } else { + i += right; + } + } + // finish the chunk with more rounds + for start in 1..gcd { + // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for + // reading and writing as per the function's safety contract, see [long-safety-expl] + // above + tmp = unsafe { x.add(start).read() }; + // [safety-expl-addition] + // + // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the + // greatest common divisor of `(left+right, right)` means that `left = right` so + // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing + // according to the function's safety contract. + i = start + right; + loop { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + tmp = unsafe { x.add(i).replace(tmp) }; + if i >= left { + i -= left; + if i == start { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + unsafe { x.add(start).write(tmp) }; + break; + } + } else { + i += right; + } + } + } +} + +/// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements. +/// +/// /// /// ```text /// left = 11, right = 4 /// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3] @@ -61,182 +214,50 @@ use crate::{cmp, ptr}; /// we cannot swap any more, but a smaller rotation problem is left to solve /// ``` /// when `left < right` the swapping happens from the left instead. -pub(super) unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) { - type BufType = [usize; 32]; - if T::IS_ZST { - return; - } - // N.B. the below algorithms can fail if these cases are not checked - if (right == 0) || (left == 0) { - return; - } - // `T` is not a zero-sized type, so it's okay to divide by its size. - if !cfg!(feature = "optimize_for_size") - && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() - { - // Algorithm 1 - // The `[T; 0]` here is to ensure this is appropriately aligned for T - let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); - let buf = rawarray.as_mut_ptr() as *mut T; - // SAFETY: `mid-left <= mid-left+right < mid+right` - let dim = unsafe { mid.sub(left).add(right) }; - if left <= right { - // SAFETY: - // - // 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in - // `buf` without overflow and `buf` was created just above and so cannot be - // overlapped with any value of `[mid-left; left]` - // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care - // about overlaps here. - // 3) The `if` condition about `left <= right` ensures writing `left` elements to - // `dim = mid-left+right` is valid because: - // - `buf` is valid and `left` elements were written in it in 1) - // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` - unsafe { - // 1) - ptr::copy_nonoverlapping(mid.sub(left), buf, left); - // 2) - ptr::copy(mid, mid.sub(left), right); - // 3) - ptr::copy_nonoverlapping(buf, dim, left); - } - } else { - // SAFETY: same reasoning as above but with `left` and `right` reversed - unsafe { - ptr::copy_nonoverlapping(mid, buf, right); - ptr::copy(mid.sub(left), dim, left); - ptr::copy_nonoverlapping(buf, mid.sub(left), right); - } - } - } else if !cfg!(feature = "optimize_for_size") - && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) - { - // Algorithm 2 - // Microbenchmarks indicate that the average performance for random shifts is better all - // the way until about `left + right == 32`, but the worst case performance breaks even - // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 - // `usize`s, this algorithm also outperforms other algorithms. - // SAFETY: callers must ensure `mid - left` is valid for reading and writing. - let x = unsafe { mid.sub(left) }; - // beginning of first round - // SAFETY: see previous comment. - let mut tmp: T = unsafe { x.read() }; - let mut i = right; - // `gcd` can be found before hand by calculating `gcd(left + right, right)`, - // but it is faster to do one loop which calculates the gcd as a side effect, then - // doing the rest of the chunk - let mut gcd = right; - // benchmarks reveal that it is faster to swap temporaries all the way through instead - // of reading one temporary once, copying backwards, and then writing that temporary at - // the very end. This is possibly due to the fact that swapping or replacing temporaries - // uses only one memory address in the loop instead of needing to manage two. - loop { - // [long-safety-expl] - // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and - // writing. - // - // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` - // - `i <= left+right-1` is always true - // - if `i < left`, `right` is added so `i < left+right` and on the next - // iteration `left` is removed from `i` so it doesn't go further - // - if `i >= left`, `left` is removed immediately and so it doesn't go further. - // - overflows cannot happen for `i` since the function's safety contract ask for - // `mid+right-1 = x+left+right` to be valid for writing - // - underflows cannot happen because `i` must be bigger or equal to `left` for - // a subtraction of `left` to happen. - // - // So `x+i` is valid for reading and writing if the caller respected the contract - tmp = unsafe { x.add(i).replace(tmp) }; - // instead of incrementing `i` and then checking if it is outside the bounds, we - // check if `i` will go outside the bounds on the next increment. This prevents - // any wrapping of pointers or `usize`. - if i >= left { - i -= left; - if i == 0 { - // end of first round - // SAFETY: tmp has been read from a valid source and x is valid for writing - // according to the caller. - unsafe { x.write(tmp) }; +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +unsafe fn ptr_rotate_swap(mut left: usize, mut mid: *mut T, mut right: usize) { + loop { + if left >= right { + // Algorithm 3 + // There is an alternate way of swapping that involves finding where the last swap + // of this algorithm would be, and swapping using that last chunk instead of swapping + // adjacent chunks like this algorithm is doing, but this way is still faster. + loop { + // SAFETY: + // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing + // Subtracting `right` from `mid` each turn is counterbalanced by the addition and + // check after it. + unsafe { + ptr::swap_nonoverlapping(mid.sub(right), mid, right); + mid = mid.sub(right); + } + left -= right; + if left < right { break; } - // this conditional must be here if `left + right >= 15` - if i < gcd { - gcd = i; - } - } else { - i += right; } - } - // finish the chunk with more rounds - for start in 1..gcd { - // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for - // reading and writing as per the function's safety contract, see [long-safety-expl] - // above - tmp = unsafe { x.add(start).read() }; - // [safety-expl-addition] - // - // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the - // greatest common divisor of `(left+right, right)` means that `left = right` so - // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing - // according to the function's safety contract. - i = start + right; + } else { + // Algorithm 3, `left < right` loop { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - tmp = unsafe { x.add(i).replace(tmp) }; - if i >= left { - i -= left; - if i == start { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - unsafe { x.add(start).write(tmp) }; - break; - } - } else { - i += right; + // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because + // `left < right` so `mid+left < mid+right`. + // Adding `left` to `mid` each turn is counterbalanced by the subtraction and check + // after it. + unsafe { + ptr::swap_nonoverlapping(mid.sub(left), mid, left); + mid = mid.add(left); + } + right -= left; + if right < left { + break; } } } - } else { - loop { - if left >= right { - // Algorithm 3 - // There is an alternate way of swapping that involves finding where the last swap - // of this algorithm would be, and swapping using that last chunk instead of swapping - // adjacent chunks like this algorithm is doing, but this way is still faster. - loop { - // SAFETY: - // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing - // Subtracting `right` from `mid` each turn is counterbalanced by the addition and - // check after it. - unsafe { - ptr::swap_nonoverlapping(mid.sub(right), mid, right); - mid = mid.sub(right); - } - left -= right; - if left < right { - break; - } - } - } else { - // Algorithm 3, `left < right` - loop { - // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because - // `left < right` so `mid+left < mid+right`. - // Adding `left` to `mid` each turn is counterbalanced by the subtraction and check - // after it. - unsafe { - ptr::swap_nonoverlapping(mid.sub(left), mid, left); - mid = mid.add(left); - } - right -= left; - if right < left { - break; - } - } - } - - if (right == 0) || (left == 0) { - return; - } + if (right == 0) || (left == 0) { + return; } } } From fb3d1d0c4bcd1b744c8ef23ba977bad9fcd43849 Mon Sep 17 00:00:00 2001 From: edwloef Date: Tue, 28 Jan 2025 12:26:32 +0100 Subject: [PATCH 258/342] add inline attribute and codegen test --- library/core/src/slice/rotate.rs | 4 +++ .../codegen/lib-optimizations/slice_rotate.rs | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/codegen/lib-optimizations/slice_rotate.rs diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index 3e88978b7810..5d5ee4c7b624 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -10,6 +10,7 @@ type BufType = [usize; 32]; /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] pub(super) unsafe fn ptr_rotate(left: usize, mid: *mut T, right: usize) { if T::IS_ZST { return; @@ -43,6 +44,7 @@ pub(super) unsafe fn ptr_rotate(left: usize, mid: *mut T, right: usize) { /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] unsafe fn ptr_rotate_memmove(left: usize, mid: *mut T, right: usize) { // The `[T; 0]` here is to ensure this is appropriately aligned for T let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); @@ -114,6 +116,7 @@ unsafe fn ptr_rotate_memmove(left: usize, mid: *mut T, right: usize) { /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] unsafe fn ptr_rotate_gcd(left: usize, mid: *mut T, right: usize) { // Algorithm 2 // Microbenchmarks indicate that the average performance for random shifts is better all @@ -218,6 +221,7 @@ unsafe fn ptr_rotate_gcd(left: usize, mid: *mut T, right: usize) { /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] unsafe fn ptr_rotate_swap(mut left: usize, mut mid: *mut T, mut right: usize) { loop { if left >= right { diff --git a/tests/codegen/lib-optimizations/slice_rotate.rs b/tests/codegen/lib-optimizations/slice_rotate.rs new file mode 100644 index 000000000000..d0a7b328d184 --- /dev/null +++ b/tests/codegen/lib-optimizations/slice_rotate.rs @@ -0,0 +1,30 @@ +//@ compile-flags: -O + +#![crate_type = "lib"] + +// Ensure that the simple case of rotating by a constant 1 optimizes to the obvious thing + +// CHECK-LABEL: @rotate_left_by_one +#[no_mangle] +pub fn rotate_left_by_one(slice: &mut [i32]) { + // CHECK-NOT: phi + // CHECK-NOT: call + // CHECK-NOT: load + // CHECK-NOT: store + // CHECK-NOT: getelementptr + // CHECK: %[[END:.+]] = getelementptr + // CHECK-NEXT: %[[DIM:.+]] = getelementptr + // CHECK-NEXT: %[[LAST:.+]] = load + // CHECK-NEXT: %[[FIRST:.+]] = shl + // CHECK-NEXT: call void @llvm.memmove + // CHECK-NEXT: store i32 %[[LAST]], ptr %[[DIM:.+]] + // CHECK-NOT: phi + // CHECK-NOT: call + // CHECK-NOT: load + // CHECK-NOT: store + // CHECK-NOT: getelementptr + // CHECK: ret void + if !slice.is_empty() { + slice.rotate_left(1); + } +} From eb457da5cafc0eb907a0ecf70ffae5a37cd4e8f0 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 11:36:36 -0700 Subject: [PATCH 259/342] rustdoc: rename `issue-\d+.rs` tests to have meaningful names --- .../intra-doc/auxiliary/issue-103463-aux.rs | 0 .../issue-103463.rs => rustdoc-ui/intra-doc/ice-103463.rs} | 1 + .../issue-104145.rs => rustdoc-ui/intra-doc/ice-104145.rs} | 1 + .../{issue-28480.rs => doc-hidden-broken-link-28480.rs} | 0 .../{issue-31948-1.rs => doc-hidden-module-impl-31948-1.rs} | 0 .../{issue-31948-2.rs => doc-hidden-module-impl-31948-2.rs} | 0 .../{issue-31948.rs => doc-hidden-module-impl-31948.rs} | 0 .../inline_cross/{issue-32881.rs => impl-dyn-trait-32881.rs} | 0 tests/rustdoc/inline_cross/{issue-33113.rs => impl-ref-33113.rs} | 0 .../inline_cross/{issue-76736-1.rs => rustc-private-76736-1.rs} | 0 .../inline_cross/{issue-76736-2.rs => rustc-private-76736-2.rs} | 0 .../inline_cross/{issue-76736-3.rs => rustc-private-76736-3.rs} | 0 .../inline_cross/{issue-76736-4.rs => rustc-private-76736-4.rs} | 0 ...l => self-sized-bounds-24183.method_no_where_self_sized.html} | 0 .../inline_cross/{issue-24183.rs => self-sized-bounds-24183.rs} | 0 .../inline_local/{issue-32343.rs => doc-no-inline-32343.rs} | 0 .../inline_local/{issue-28537.rs => pub-re-export-28537.rs} | 0 tests/rustdoc/intra-doc/{issue-82209.rs => enum-self-82209.rs} | 0 ...08459.rs => link-same-name-different-disambiguator-108459.rs} | 0 .../{issue-66159.rs => same-name-different-crates-66159.rs} | 0 20 files changed, 2 insertions(+) rename tests/{rustdoc => rustdoc-ui}/intra-doc/auxiliary/issue-103463-aux.rs (100%) rename tests/{rustdoc/intra-doc/issue-103463.rs => rustdoc-ui/intra-doc/ice-103463.rs} (94%) rename tests/{rustdoc/intra-doc/issue-104145.rs => rustdoc-ui/intra-doc/ice-104145.rs} (95%) rename tests/rustdoc/inline_cross/{issue-28480.rs => doc-hidden-broken-link-28480.rs} (100%) rename tests/rustdoc/inline_cross/{issue-31948-1.rs => doc-hidden-module-impl-31948-1.rs} (100%) rename tests/rustdoc/inline_cross/{issue-31948-2.rs => doc-hidden-module-impl-31948-2.rs} (100%) rename tests/rustdoc/inline_cross/{issue-31948.rs => doc-hidden-module-impl-31948.rs} (100%) rename tests/rustdoc/inline_cross/{issue-32881.rs => impl-dyn-trait-32881.rs} (100%) rename tests/rustdoc/inline_cross/{issue-33113.rs => impl-ref-33113.rs} (100%) rename tests/rustdoc/inline_cross/{issue-76736-1.rs => rustc-private-76736-1.rs} (100%) rename tests/rustdoc/inline_cross/{issue-76736-2.rs => rustc-private-76736-2.rs} (100%) rename tests/rustdoc/inline_cross/{issue-76736-3.rs => rustc-private-76736-3.rs} (100%) rename tests/rustdoc/inline_cross/{issue-76736-4.rs => rustc-private-76736-4.rs} (100%) rename tests/rustdoc/inline_cross/{issue-24183.method_no_where_self_sized.html => self-sized-bounds-24183.method_no_where_self_sized.html} (100%) rename tests/rustdoc/inline_cross/{issue-24183.rs => self-sized-bounds-24183.rs} (100%) rename tests/rustdoc/inline_local/{issue-32343.rs => doc-no-inline-32343.rs} (100%) rename tests/rustdoc/inline_local/{issue-28537.rs => pub-re-export-28537.rs} (100%) rename tests/rustdoc/intra-doc/{issue-82209.rs => enum-self-82209.rs} (100%) rename tests/rustdoc/intra-doc/{issue-108459.rs => link-same-name-different-disambiguator-108459.rs} (100%) rename tests/rustdoc/intra-doc/{issue-66159.rs => same-name-different-crates-66159.rs} (100%) diff --git a/tests/rustdoc/intra-doc/auxiliary/issue-103463-aux.rs b/tests/rustdoc-ui/intra-doc/auxiliary/issue-103463-aux.rs similarity index 100% rename from tests/rustdoc/intra-doc/auxiliary/issue-103463-aux.rs rename to tests/rustdoc-ui/intra-doc/auxiliary/issue-103463-aux.rs diff --git a/tests/rustdoc/intra-doc/issue-103463.rs b/tests/rustdoc-ui/intra-doc/ice-103463.rs similarity index 94% rename from tests/rustdoc/intra-doc/issue-103463.rs rename to tests/rustdoc-ui/intra-doc/ice-103463.rs index bfe5c4f05d4d..10894282e55b 100644 --- a/tests/rustdoc/intra-doc/issue-103463.rs +++ b/tests/rustdoc-ui/intra-doc/ice-103463.rs @@ -1,4 +1,5 @@ // https://github.com/rust-lang/rust/issues/103463 +//@ check-pass // The `Trait` is not pulled into the crate resulting in doc links in its methods being resolved. diff --git a/tests/rustdoc/intra-doc/issue-104145.rs b/tests/rustdoc-ui/intra-doc/ice-104145.rs similarity index 95% rename from tests/rustdoc/intra-doc/issue-104145.rs rename to tests/rustdoc-ui/intra-doc/ice-104145.rs index 92c7fa31b4fe..0e403b21c8a1 100644 --- a/tests/rustdoc/intra-doc/issue-104145.rs +++ b/tests/rustdoc-ui/intra-doc/ice-104145.rs @@ -1,4 +1,5 @@ // https://github.com/rust-lang/rust/issues/104145 +//@ check-pass // Doc links in `Trait`'s methods are resolved because it has a local impl. diff --git a/tests/rustdoc/inline_cross/issue-28480.rs b/tests/rustdoc/inline_cross/doc-hidden-broken-link-28480.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-28480.rs rename to tests/rustdoc/inline_cross/doc-hidden-broken-link-28480.rs diff --git a/tests/rustdoc/inline_cross/issue-31948-1.rs b/tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-1.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-31948-1.rs rename to tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-1.rs diff --git a/tests/rustdoc/inline_cross/issue-31948-2.rs b/tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-2.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-31948-2.rs rename to tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-2.rs diff --git a/tests/rustdoc/inline_cross/issue-31948.rs b/tests/rustdoc/inline_cross/doc-hidden-module-impl-31948.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-31948.rs rename to tests/rustdoc/inline_cross/doc-hidden-module-impl-31948.rs diff --git a/tests/rustdoc/inline_cross/issue-32881.rs b/tests/rustdoc/inline_cross/impl-dyn-trait-32881.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-32881.rs rename to tests/rustdoc/inline_cross/impl-dyn-trait-32881.rs diff --git a/tests/rustdoc/inline_cross/issue-33113.rs b/tests/rustdoc/inline_cross/impl-ref-33113.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-33113.rs rename to tests/rustdoc/inline_cross/impl-ref-33113.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-1.rs b/tests/rustdoc/inline_cross/rustc-private-76736-1.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-76736-1.rs rename to tests/rustdoc/inline_cross/rustc-private-76736-1.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-2.rs b/tests/rustdoc/inline_cross/rustc-private-76736-2.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-76736-2.rs rename to tests/rustdoc/inline_cross/rustc-private-76736-2.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-3.rs b/tests/rustdoc/inline_cross/rustc-private-76736-3.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-76736-3.rs rename to tests/rustdoc/inline_cross/rustc-private-76736-3.rs diff --git a/tests/rustdoc/inline_cross/issue-76736-4.rs b/tests/rustdoc/inline_cross/rustc-private-76736-4.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-76736-4.rs rename to tests/rustdoc/inline_cross/rustc-private-76736-4.rs diff --git a/tests/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html b/tests/rustdoc/inline_cross/self-sized-bounds-24183.method_no_where_self_sized.html similarity index 100% rename from tests/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html rename to tests/rustdoc/inline_cross/self-sized-bounds-24183.method_no_where_self_sized.html diff --git a/tests/rustdoc/inline_cross/issue-24183.rs b/tests/rustdoc/inline_cross/self-sized-bounds-24183.rs similarity index 100% rename from tests/rustdoc/inline_cross/issue-24183.rs rename to tests/rustdoc/inline_cross/self-sized-bounds-24183.rs diff --git a/tests/rustdoc/inline_local/issue-32343.rs b/tests/rustdoc/inline_local/doc-no-inline-32343.rs similarity index 100% rename from tests/rustdoc/inline_local/issue-32343.rs rename to tests/rustdoc/inline_local/doc-no-inline-32343.rs diff --git a/tests/rustdoc/inline_local/issue-28537.rs b/tests/rustdoc/inline_local/pub-re-export-28537.rs similarity index 100% rename from tests/rustdoc/inline_local/issue-28537.rs rename to tests/rustdoc/inline_local/pub-re-export-28537.rs diff --git a/tests/rustdoc/intra-doc/issue-82209.rs b/tests/rustdoc/intra-doc/enum-self-82209.rs similarity index 100% rename from tests/rustdoc/intra-doc/issue-82209.rs rename to tests/rustdoc/intra-doc/enum-self-82209.rs diff --git a/tests/rustdoc/intra-doc/issue-108459.rs b/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs similarity index 100% rename from tests/rustdoc/intra-doc/issue-108459.rs rename to tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs diff --git a/tests/rustdoc/intra-doc/issue-66159.rs b/tests/rustdoc/intra-doc/same-name-different-crates-66159.rs similarity index 100% rename from tests/rustdoc/intra-doc/issue-66159.rs rename to tests/rustdoc/intra-doc/same-name-different-crates-66159.rs From 52519e145ef3496781fa049ea0edbe44a1bc206c Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 29 Jan 2025 19:47:59 +0100 Subject: [PATCH 260/342] Cleanup docs for Allocator --- library/core/src/alloc/mod.rs | 58 ++++++++++++++++------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index aa841db045ce..dcab6136ae8a 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -49,26 +49,26 @@ impl fmt::Display for AllocError { /// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of /// data described via [`Layout`][]. /// -/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having -/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers. +/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the /// allocated memory. /// -/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying -/// allocator does not support this (like jemalloc) or return a null pointer (such as -/// `libc::malloc`), this must be caught by the implementation. +/// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying +/// allocator does not support this (like jemalloc) or responds by returning a null pointer +/// (such as `libc::malloc`), this must be caught by the implementation. /// /// ### Currently allocated memory /// -/// Some of the methods require that a memory block be *currently allocated* via an allocator. This -/// means that: +/// Some of the methods require that a memory block is *currently allocated* by an allocator. +/// This means that: +/// * the starting address for that memory block was previously +/// returned by [`allocate`], [`grow`], or [`shrink`], and +/// * the memory block has not subsequently been deallocated. /// -/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or -/// [`shrink`], and -/// -/// * the memory block has not been subsequently deallocated, where blocks are either deallocated -/// directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or -/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer -/// remains valid. +/// A memory block is deallocated by a call to [`deallocate`], +/// or by a call to [`grow`] or [`shrink`] that returns `Ok`. +/// A call to `grow` or `shrink` that returns `Err`, +/// does not deallocate the memory block passed to it. /// /// [`allocate`]: Allocator::allocate /// [`grow`]: Allocator::grow @@ -77,32 +77,28 @@ impl fmt::Display for AllocError { /// /// ### Memory fitting /// -/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to -/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the +/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the /// following conditions must hold: -/// -/// * The block must be allocated with the same alignment as [`layout.align()`], and -/// -/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: -/// - `min` is the size of the layout most recently used to allocate the block, and -/// - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`]. +/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and +/// * [`layout.size()`] must fall in the range `min ..= max`, where: +/// - `min` is the size of the layout used to allocate the block, and +/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`]. /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size /// /// # Safety /// -/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to -/// valid memory and retain their validity while they are [*currently allocated*] and the shorter -/// of: -/// - the borrow-checker lifetime of the allocator type itself. -/// - as long as at least one of the instance and all of its clones has not been dropped. +/// Memory blocks that are [*currently allocated*] by an allocator, +/// must point to valid memory, and retain their validity while until either: +/// - the memory block is deallocated, or +/// - the allocator is dropped. /// -/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this -/// allocator. A copied or cloned allocator must behave like the same allocator, and +/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it +/// A copied or cloned allocator must behave like the original allocator. /// -/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other -/// method of the allocator. +/// A memory block which is [*currently allocated*] may be passed to +/// any method of the allocator that accepts such an argument. /// /// [*currently allocated*]: #currently-allocated-memory #[unstable(feature = "allocator_api", issue = "32838")] From aeabd4bebe5a129ab226ee4f5e834a9ce001437f Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:26:39 +0100 Subject: [PATCH 261/342] ci: use windows 2025 for i686-mingw --- src/ci/github-actions/jobs.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index c6bc19f9e6c4..9af887096251 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -43,6 +43,10 @@ runners: os: windows-2022-8core-32gb <<: *base-job + - &job-windows-25-8c + os: windows-2025-8core-32gb + <<: *base-job + - &job-aarch64-linux # Free some disk space to avoid running out of space during the build. free_disk: true @@ -524,7 +528,7 @@ auto: # We are intentionally allowing an old toolchain on this builder (and that's # incompatible with LLVM downloads today). NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-8c + <<: *job-windows-25-8c # x86_64-mingw is split into two jobs to run tests in parallel. - name: x86_64-mingw-1 From 022c0ce94787a76587fd39519f1358acd98fed49 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 30 Jan 2025 09:12:35 +1100 Subject: [PATCH 262/342] Remove `NamedVarMap`. `NamedVarMap` is extremely similar to `ResolveBoundVars`. The former contains two `UnordMap` fields (obscured behind `ItemLocalMap` typedefs). The latter contains two `SortedMap` fields. We construct a `NamedVarMap` and then convert it into a `ResolveBoundVars` by sorting the `UnordMap`s, which is unnecessary busywork. This commit removes `NamedVarMap` and constructs a `ResolveBoundVars` directly. `SortedMap` and `NamedVarMap` have slightly different perf characteristics during construction (e.g. speed of insertion) but this code isn't hot enough for that to matter. A few details to note. - A `FIXME` comment is removed. - The detailed comments on the fields of `NamedVarMap` are copied to `ResolveBoundVars` (which has a single, incorrect comment). - `BoundVarContext::map` is renamed. - `ResolveBoundVars` gets a derived `Default` impl. --- .../src/collect/resolve_bound_vars.rs | 94 +++++-------------- .../src/middle/resolve_bound_vars.rs | 15 ++- 2 files changed, 37 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 72baf5c4b58d..d67b9d335960 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -12,13 +12,11 @@ use std::ops::ControlFlow; use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; use rustc_hir::{ - self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, - LifetimeName, Node, + self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, LifetimeName, Node, }; use rustc_macros::extension; use rustc_middle::hir::nested_filter; @@ -26,7 +24,7 @@ use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_middle::{bug, span_bug}; -use rustc_span::def_id::{DefId, LocalDefId, LocalDefIdMap}; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{Ident, Span, sym}; use tracing::{debug, debug_span, instrument}; @@ -62,33 +60,9 @@ impl ResolvedArg { } } -/// Maps the id of each bound variable reference to the variable decl -/// that it corresponds to. -/// -/// FIXME. This struct gets converted to a `ResolveBoundVars` for -/// actual use. It has the same data, but indexed by `LocalDefId`. This -/// is silly. -#[derive(Debug, Default)] -struct NamedVarMap { - // maps from every use of a named (not anonymous) bound var to a - // `ResolvedArg` describing how that variable is bound - defs: ItemLocalMap, - - // Maps relevant hir items to the bound vars on them. These include: - // - function defs - // - function pointers - // - closures - // - trait refs - // - bound types (like `T` in `for<'a> T<'a>: Foo`) - late_bound_vars: ItemLocalMap>, - - // List captured variables for each opaque type. - opaque_captured_lifetimes: LocalDefIdMap>, -} - struct BoundVarContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, - map: &'a mut NamedVarMap, + rbv: &'a mut ResolveBoundVars, scope: ScopeRef<'a>, } @@ -267,19 +241,12 @@ pub(crate) fn provide(providers: &mut Providers) { /// Computes the `ResolveBoundVars` map that contains data for an entire `Item`. /// You should not read the result of this query directly, but rather use -/// `named_variable_map`, `is_late_bound_map`, etc. +/// `named_variable_map`, `late_bound_vars_map`, etc. #[instrument(level = "debug", skip(tcx))] fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars { - let mut named_variable_map = NamedVarMap { - defs: Default::default(), - late_bound_vars: Default::default(), - opaque_captured_lifetimes: Default::default(), - }; - let mut visitor = BoundVarContext { - tcx, - map: &mut named_variable_map, - scope: &Scope::Root { opt_parent_item: None }, - }; + let mut rbv = ResolveBoundVars::default(); + let mut visitor = + BoundVarContext { tcx, rbv: &mut rbv, scope: &Scope::Root { opt_parent_item: None } }; match tcx.hir_owner_node(local_def_id) { hir::OwnerNode::Item(item) => visitor.visit_item(item), hir::OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), @@ -299,19 +266,10 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou hir::OwnerNode::Synthetic => unreachable!(), } - let defs = named_variable_map.defs.into_sorted_stable_ord(); - let late_bound_vars = named_variable_map.late_bound_vars.into_sorted_stable_ord(); - let opaque_captured_lifetimes = named_variable_map.opaque_captured_lifetimes; - let rl = ResolveBoundVars { - defs: SortedMap::from_presorted_elements(defs), - late_bound_vars: SortedMap::from_presorted_elements(late_bound_vars), - opaque_captured_lifetimes, - }; - - debug!(?rl.defs); - debug!(?rl.late_bound_vars); - debug!(?rl.opaque_captured_lifetimes); - rl + debug!(?rbv.defs); + debug!(?rbv.late_bound_vars); + debug!(?rbv.opaque_captured_lifetimes); + rbv } fn late_arg_as_bound_arg<'tcx>( @@ -404,7 +362,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Binder { hir_id, .. } => { // Nested poly trait refs have the binders concatenated let mut full_binders = - self.map.late_bound_vars.entry(hir_id.local_id).or_default().clone(); + self.rbv.late_bound_vars.get_mut_or_insert_default(hir_id.local_id).clone(); full_binders.extend(supertrait_bound_vars); break (full_binders, BinderScopeType::Concatenating); } @@ -646,7 +604,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let captures = captures.into_inner().into_iter().collect(); debug!(?captures); - self.map.opaque_captured_lifetimes.insert(opaque.def_id, captures); + self.rbv.opaque_captured_lifetimes.insert(opaque.def_id, captures); } #[instrument(level = "debug", skip(self))] @@ -848,7 +806,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { hir::TyKind::Ref(lifetime_ref, ref mt) => { self.visit_lifetime(lifetime_ref); let scope = Scope::ObjectLifetimeDefault { - lifetime: self.map.defs.get(&lifetime_ref.hir_id.local_id).cloned(), + lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).cloned(), s: self.scope, }; self.with(scope, |this| this.visit_ty_unambig(mt.ty)); @@ -966,7 +924,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let bound_vars: Vec<_> = self.tcx.fn_sig(sig_id).skip_binder().bound_vars().iter().collect(); let hir_id = self.tcx.local_def_id_to_hir_id(def_id); - self.map.late_bound_vars.insert(hir_id.local_id, bound_vars); + self.rbv.late_bound_vars.insert(hir_id.local_id, bound_vars); } self.visit_fn_like_elision(fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); intravisit::walk_fn_kind(self, fk); @@ -1140,8 +1098,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { where F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>), { - let BoundVarContext { tcx, map, .. } = self; - let mut this = BoundVarContext { tcx: *tcx, map, scope: &wrap_scope }; + let BoundVarContext { tcx, rbv, .. } = self; + let mut this = BoundVarContext { tcx: *tcx, rbv, scope: &wrap_scope }; let span = debug_span!("scope", scope = ?this.scope.debug_truncated()); { let _enter = span.enter(); @@ -1150,10 +1108,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec) { - if let Some(old) = self.map.late_bound_vars.insert(hir_id.local_id, binder) { + if let Some(old) = self.rbv.late_bound_vars.insert(hir_id.local_id, binder) { bug!( "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", - self.map.late_bound_vars[&hir_id.local_id] + self.rbv.late_bound_vars[&hir_id.local_id] ) } } @@ -1597,9 +1555,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { kind.descr(param_def_id.to_def_id()) ), }; - self.map.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); + self.rbv.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); } else { - self.map.defs.insert(hir_id.local_id, def); + self.rbv.defs.insert(hir_id.local_id, def); } return; } @@ -1632,7 +1590,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { bug!("unexpected def-kind: {}", kind.descr(param_def_id.to_def_id())) } }); - self.map.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); + self.rbv.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); return; } Scope::Root { .. } => break, @@ -1725,7 +1683,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } }; - let map = &self.map; + let rbv = &self.rbv; let generics = self.tcx.generics_of(def_id); // `type_def_id` points to an item, so there is nothing to inherit generics from. @@ -1744,7 +1702,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // This index can be used with `generic_args` since `parent_count == 0`. let index = generics.param_def_id_to_index[¶m_def_id] as usize; generic_args.args.get(index).and_then(|arg| match arg { - GenericArg::Lifetime(lt) => map.defs.get(<.hir_id.local_id).copied(), + GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), _ => None, }) } @@ -2042,7 +2000,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: ResolvedArg) { debug!(span = ?lifetime_ref.ident.span); - self.map.defs.insert(lifetime_ref.hir_id.local_id, def); + self.rbv.defs.insert(lifetime_ref.hir_id.local_id, def); } // When we have a return type notation type in a where clause, like @@ -2197,7 +2155,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // See where these vars are used in `HirTyLowerer::lower_ty_maybe_return_type_notation`. // And this is exercised in: // `tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs`. - let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id.local_id).unwrap(); + let existing_bound_vars = self.rbv.late_bound_vars.get_mut(&hir_id.local_id).unwrap(); let existing_bound_vars_saved = existing_bound_vars.clone(); existing_bound_vars.extend(bound_vars); self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved); diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs index 111ac990bc77..51a079e8bc12 100644 --- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs +++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs @@ -45,15 +45,22 @@ pub enum ObjectLifetimeDefault { Param(DefId), } -/// Maps the id of each lifetime reference to the lifetime decl +/// Maps the id of each bound variable reference to the variable decl /// that it corresponds to. -#[derive(HashStable, Debug)] +#[derive(Debug, Default, HashStable)] pub struct ResolveBoundVars { - /// Maps from every use of a named (not anonymous) lifetime to a - /// `Region` describing how that region is bound + // Maps from every use of a named (not anonymous) bound var to a + // `ResolvedArg` describing how that variable is bound. pub defs: SortedMap, + // Maps relevant hir items to the bound vars on them. These include: + // - function defs + // - function pointers + // - closures + // - trait refs + // - bound types (like `T` in `for<'a> T<'a>: Foo`) pub late_bound_vars: SortedMap>, + // List captured variables for each opaque type. pub opaque_captured_lifetimes: LocalDefIdMap>, } From 3899d14db6e882f097682819a55ba30e4bc5b7d9 Mon Sep 17 00:00:00 2001 From: cyrgani <85427285+cyrgani@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:51:03 +0100 Subject: [PATCH 263/342] fix broken release notes id --- RELEASES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2da6ed3f1006..ccd286ef0a61 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,8 +1,7 @@ Version 1.84.0 (2025-01-09) ========================== - + Language -------- From b320e1741c738363184c87657f972a739d704908 Mon Sep 17 00:00:00 2001 From: Sky Date: Wed, 29 Jan 2025 20:23:59 -0500 Subject: [PATCH 264/342] Remove minor future footgun in `impl Debug for MaybeUninit` No longer breaks if `MaybeUninit` moves modules (technically it could break if `MaybeUninit` were renamed but realistically that will never happen) --- library/core/src/mem/maybe_uninit.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index ac5307a671d2..0d8c3ef906bf 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -276,10 +276,9 @@ impl Clone for MaybeUninit { impl fmt::Debug for MaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // NB: there is no `.pad_fmt` so we can't use a simpler `format_args!("MaybeUninit<{..}>"). - // This needs to be adjusted if `MaybeUninit` moves modules. let full_name = type_name::(); - let short_name = full_name.split_once("mem::maybe_uninit::").unwrap().1; - f.pad(short_name) + let prefix_len = full_name.find("MaybeUninit").unwrap(); + f.pad(&full_name[prefix_len..]) } } From 1f30517d40a9a8fe3b89479891c7a167adb75cbd Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 29 Jan 2025 21:31:13 -0500 Subject: [PATCH 265/342] upstream rustc_codegen_ssa/rustc_middle changes for enzyme/autodiff --- Cargo.lock | 2 + .../rustc_ast/src/expand/autodiff_attrs.rs | 3 +- .../src/builder/autodiff.rs | 16 +-- .../src/coverageinfo/mapgen.rs | 2 +- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 4 +- compiler/rustc_codegen_ssa/messages.ftl | 2 + compiler/rustc_codegen_ssa/src/back/write.rs | 38 +++++- compiler/rustc_codegen_ssa/src/base.rs | 10 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 118 ++++++++++++++++- compiler/rustc_codegen_ssa/src/errors.rs | 4 + compiler/rustc_interface/src/tests.rs | 13 +- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 5 +- compiler/rustc_middle/messages.ftl | 4 + compiler/rustc_middle/src/arena.rs | 1 + compiler/rustc_middle/src/error.rs | 14 ++ .../src/middle/codegen_fn_attrs.rs | 4 + compiler/rustc_middle/src/mir/mono.rs | 2 + compiler/rustc_monomorphize/Cargo.toml | 2 + compiler/rustc_monomorphize/src/collector.rs | 2 +- .../rustc_monomorphize/src/partitioning.rs | 32 ++++- .../src/partitioning/autodiff.rs | 121 ++++++++++++++++++ compiler/rustc_session/src/config.rs | 36 +++++- compiler/rustc_session/src/options.rs | 49 +++++++ compiler/rustc_span/src/symbol.rs | 2 - src/bootstrap/src/core/build_steps/compile.rs | 9 +- .../src/compiler-flags/autodiff.md | 23 ++++ src/tools/enzyme | 2 +- 27 files changed, 482 insertions(+), 38 deletions(-) create mode 100644 compiler/rustc_monomorphize/src/partitioning/autodiff.rs create mode 100644 src/doc/unstable-book/src/compiler-flags/autodiff.md diff --git a/Cargo.lock b/Cargo.lock index 979198cece80..7526cca9c4b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4234,6 +4234,7 @@ name = "rustc_monomorphize" version = "0.0.0" dependencies = [ "rustc_abi", + "rustc_ast", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4243,6 +4244,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "serde", "serde_json", diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index 7ef8bc179738..ecc522ec39d1 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -79,6 +79,7 @@ pub struct AutoDiffItem { pub target: String, pub attrs: AutoDiffAttrs, } + #[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct AutoDiffAttrs { /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and @@ -231,7 +232,7 @@ impl AutoDiffAttrs { self.ret_activity == DiffActivity::ActiveOnly } - pub fn error() -> Self { + pub const fn error() -> Self { AutoDiffAttrs { mode: DiffMode::Error, ret_activity: DiffActivity::None, diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 6b17b5f6989b..9e8e4e1c5677 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -62,8 +62,8 @@ fn generate_enzyme_call<'ll>( // add outer_fn name to ad_name to make it unique, in case users apply autodiff to multiple // functions. Unwrap will only panic, if LLVM gave us an invalid string. let name = llvm::get_value_name(outer_fn); - let outer_fn_name = std::ffi::CStr::from_bytes_with_nul(name).unwrap().to_str().unwrap(); - ad_name.push_str(outer_fn_name.to_string().as_str()); + let outer_fn_name = std::str::from_utf8(name).unwrap(); + ad_name.push_str(outer_fn_name); // Let us assume the user wrote the following function square: // @@ -255,14 +255,14 @@ fn generate_enzyme_call<'ll>( // have no debug info to copy, which would then be ok. trace!("no dbg info"); } - // Now that we copied the metadata, get rid of dummy code. - llvm::LLVMRustEraseInstBefore(entry, last_inst); - llvm::LLVMRustEraseInstFromParent(last_inst); - if cx.val_ty(outer_fn) != cx.type_void() { - builder.ret(call); - } else { + // Now that we copied the metadata, get rid of dummy code. + llvm::LLVMRustEraseInstUntilInclusive(entry, last_inst); + + if cx.val_ty(call) == cx.type_void() { builder.ret_void(); + } else { + builder.ret(call); } // Let's crash in case that we messed something up above and generated invalid IR. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index fd22421c7fcf..9a2473d6cf23 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -298,7 +298,7 @@ struct UsageSets<'tcx> { /// Prepare sets of definitions that are relevant to deciding whether something /// is an "unused function" for coverage purposes. fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { - let MonoItemPartitions { all_mono_items, codegen_units } = + let MonoItemPartitions { all_mono_items, codegen_units, .. } = tcx.collect_and_partition_mono_items(()); // Obtain a MIR body for each function participating in codegen, via an diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 729d6f62e243..ae813fe5ebf8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -7,11 +7,13 @@ use crate::llvm::Bool; extern "C" { // Enzyme pub fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool; - pub fn LLVMRustEraseInstBefore(BB: &BasicBlock, I: &Value); + pub fn LLVMRustEraseInstUntilInclusive(BB: &BasicBlock, I: &Value); pub fn LLVMRustGetLastInstruction<'a>(BB: &BasicBlock) -> Option<&'a Value>; pub fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>; pub fn LLVMRustEraseInstFromParent(V: &Value); pub fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; + pub fn LLVMDumpModule(M: &Module); + pub fn LLVMDumpValue(V: &Value); pub fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; pub fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index de37de09f5a5..4c5664f68888 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,8 @@ codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$erro codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering +codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto + codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty codegen_ssa_cgu_not_recorded = diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index b40bb4ed5d2a..914f2c21fa78 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -7,6 +7,7 @@ use std::sync::mpsc::{Receiver, Sender, channel}; use std::{fs, io, mem, str, thread}; use rustc_ast::attr; +use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; @@ -40,7 +41,7 @@ use tracing::debug; use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; use super::symbol_export::symbol_name_for_instance_in_crate; -use crate::errors::ErrorCreatingRemarkDir; +use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir}; use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, @@ -118,6 +119,7 @@ pub struct ModuleConfig { pub merge_functions: bool, pub emit_lifetime_markers: bool, pub llvm_plugins: Vec, + pub autodiff: Vec, } impl ModuleConfig { @@ -266,6 +268,7 @@ impl ModuleConfig { emit_lifetime_markers: sess.emit_lifetime_markers(), llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]), + autodiff: if_regular!(sess.opts.unstable_opts.autodiff.clone(), vec![]), } } @@ -389,6 +392,7 @@ impl CodegenContext { fn generate_lto_work( cgcx: &CodegenContext, + autodiff: Vec, needs_fat_lto: Vec>, needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, @@ -397,11 +401,19 @@ fn generate_lto_work( if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); - let module = + let mut module = B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + if cgcx.lto == Lto::Fat { + let config = cgcx.config(ModuleKind::Regular); + module = unsafe { module.autodiff(cgcx, autodiff, config).unwrap() }; + } // We are adding a single work item, so the cost doesn't matter. vec![(WorkItem::LTO(module), 0)] } else { + if !autodiff.is_empty() { + let dcx = cgcx.create_dcx(); + dcx.handle().emit_fatal(AutodiffWithoutLto {}); + } assert!(needs_fat_lto.is_empty()); let (lto_modules, copy_jobs) = B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules) .unwrap_or_else(|e| e.raise()); @@ -1021,6 +1033,9 @@ pub(crate) enum Message { /// Sent from a backend worker thread. WorkItem { result: Result, Option>, worker_id: usize }, + /// A vector containing all the AutoDiff tasks that we have to pass to Enzyme. + AddAutoDiffItems(Vec), + /// The frontend has finished generating something (backend IR or a /// post-LTO artifact) for a codegen unit, and it should be passed to the /// backend. Sent from the main thread. @@ -1348,6 +1363,7 @@ fn start_executing_work( // This is where we collect codegen units that have gone all the way // through codegen and LLVM. + let mut autodiff_items = Vec::new(); let mut compiled_modules = vec![]; let mut compiled_allocator_module = None; let mut needs_link = Vec::new(); @@ -1459,9 +1475,13 @@ fn start_executing_work( let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); - for (work, cost) in - generate_lto_work(&cgcx, needs_fat_lto, needs_thin_lto, import_only_modules) - { + for (work, cost) in generate_lto_work( + &cgcx, + autodiff_items.clone(), + needs_fat_lto, + needs_thin_lto, + import_only_modules, + ) { let insertion_index = work_items .binary_search_by_key(&cost, |&(_, cost)| cost) .unwrap_or_else(|e| e); @@ -1596,6 +1616,10 @@ fn start_executing_work( main_thread_state = MainThreadState::Idle; } + Message::AddAutoDiffItems(mut items) => { + autodiff_items.append(&mut items); + } + Message::CodegenComplete => { if codegen_state != Aborted { codegen_state = Completed; @@ -2070,6 +2094,10 @@ impl OngoingCodegen { drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::))); } + pub(crate) fn submit_autodiff_items(&self, items: Vec) { + drop(self.coordinator.sender.send(Box::new(Message::::AddAutoDiffItems(items)))); + } + pub(crate) fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index e438bd70c510..8a15acfcad7f 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -18,7 +18,7 @@ use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, Debugger use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::{exported_symbols, lang_items}; use rustc_middle::mir::BinOp; -use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -619,7 +619,9 @@ pub fn codegen_crate( // Run the monomorphization collector and partition the collected items into // codegen units. - let codegen_units = tcx.collect_and_partition_mono_items(()).codegen_units; + let MonoItemPartitions { codegen_units, autodiff_items, .. } = + tcx.collect_and_partition_mono_items(()); + let autodiff_fncs = autodiff_items.to_vec(); // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -690,6 +692,10 @@ pub fn codegen_crate( ); } + if !autodiff_fncs.is_empty() { + ongoing_codegen.submit_autodiff_items(autodiff_fncs); + } + // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization // by, for example, preventing a large CGU from being processed last and diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a0bc2d4ea48f..4166387dad0c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,5 +1,10 @@ +use std::str::FromStr; + use rustc_ast::attr::list_contains_name; -use rustc_ast::{MetaItemInner, attr}; +use rustc_ast::expand::autodiff_attrs::{ + AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, +}; +use rustc_ast::{MetaItem, MetaItemInner, attr}; use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; @@ -13,6 +18,7 @@ use rustc_middle::middle::codegen_fn_attrs::{ }; use rustc_middle::mir::mono::Linkage; use rustc_middle::query::Providers; +use rustc_middle::span_bug; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_session::{Session, lint}; @@ -65,6 +71,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } + // If our rustc version supports autodiff/enzyme, then we call our handler + // to check for any `#[rustc_autodiff(...)]` attributes. + if cfg!(llvm_enzyme) { + let ad = autodiff_attrs(tcx, did.into()); + codegen_fn_attrs.autodiff_item = ad; + } + // When `no_builtins` is applied at the crate level, we should add the // `no-builtins` attribute to each function to ensure it takes effect in LTO. let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); @@ -856,6 +869,109 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { } } +/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] +/// macros. There are two forms. The pure one without args to mark primal functions (the functions +/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the +/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never +/// panic, unless we introduced a bug when parsing the autodiff macro. +fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { + let attrs = tcx.get_attrs(id, sym::rustc_autodiff); + + let attrs = + attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::>(); + + // check for exactly one autodiff attribute on placeholder functions. + // There should only be one, since we generate a new placeholder per ad macro. + // FIXME(ZuseZ4): re-enable this check. Currently we add multiple, which doesn't cause harm but + // looks strange e.g. under cargo-expand. + let attr = match &attrs[..] { + [] => return None, + [attr] => attr, + // These two attributes are the same and unfortunately duplicated due to a previous bug. + [attr, _attr2] => attr, + _ => { + //FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute + //branch above. + span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source"); + } + }; + + let list = attr.meta_item_list().unwrap_or_default(); + + // empty autodiff attribute macros (i.e. `#[autodiff]`) are used to mark source functions + if list.is_empty() { + return Some(AutoDiffAttrs::source()); + } + + let [mode, input_activities @ .., ret_activity] = &list[..] else { + span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities"); + }; + let mode = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = mode { + p1.segments.first().unwrap().ident + } else { + span_bug!(attr.span, "rustc_autodiff attribute must contain mode"); + }; + + // parse mode + let mode = match mode.as_str() { + "Forward" => DiffMode::Forward, + "Reverse" => DiffMode::Reverse, + "ForwardFirst" => DiffMode::ForwardFirst, + "ReverseFirst" => DiffMode::ReverseFirst, + _ => { + span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode"); + } + }; + + // First read the ret symbol from the attribute + let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = ret_activity { + p1.segments.first().unwrap().ident + } else { + span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity"); + }; + + // Then parse it into an actual DiffActivity + let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else { + span_bug!(ret_symbol.span, "invalid return activity"); + }; + + // Now parse all the intermediate (input) activities + let mut arg_activities: Vec = vec![]; + for arg in input_activities { + let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p2, .. }) = arg { + match p2.segments.first() { + Some(x) => x.ident, + None => { + span_bug!( + arg.span(), + "rustc_autodiff attribute must contain the input activity" + ); + } + } + } else { + span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity"); + }; + + match DiffActivity::from_str(arg_symbol.as_str()) { + Ok(arg_activity) => arg_activities.push(arg_activity), + Err(_) => { + span_bug!(arg_symbol.span, "invalid input activity"); + } + } + } + + for &input in &arg_activities { + if !valid_input_activity(mode, input) { + span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode); + } + } + if !valid_ret_activity(mode, ret_activity) { + span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode); + } + + Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities }) +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 5e684632fb24..d8e44b7481a7 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -39,6 +39,10 @@ pub(crate) struct CguNotRecorded<'a> { pub cgu_name: &'a str, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_autodiff_without_lto)] +pub struct AutodiffWithoutLto; + #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] pub(crate) struct UnknownReuseKind { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 74d02ac22276..629be2cd9636 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -8,12 +8,12 @@ use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, registry}; use rustc_session::config::{ - BranchProtection, CFGuard, Cfg, CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, - DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, - FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, - LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, - OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, - PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, + AutoDiff, BranchProtection, CFGuard, Cfg, CollapseMacroDebuginfo, CoverageLevel, + CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, + Externs, FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, + InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, + NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, + Passes, PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, build_configuration, build_session_options, rustc_optgroups, }; @@ -760,6 +760,7 @@ fn test_unstable_options_tracking_hash() { tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(assume_incomplete_release, true); + tracked!(autodiff, vec![AutoDiff::Print]); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); tracked!( diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 35186778671b..4cb2376db628 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -955,7 +955,8 @@ extern "C" LLVMValueRef LLVMRustGetLastInstruction(LLVMBasicBlockRef BB) { return nullptr; } -extern "C" void LLVMRustEraseInstBefore(LLVMBasicBlockRef bb, LLVMValueRef I) { +extern "C" void LLVMRustEraseInstUntilInclusive(LLVMBasicBlockRef bb, + LLVMValueRef I) { auto &BB = *unwrap(bb); auto &Inst = *unwrap(I); auto It = BB.begin(); @@ -963,8 +964,6 @@ extern "C" void LLVMRustEraseInstBefore(LLVMBasicBlockRef bb, LLVMValueRef I) { ++It; // Make sure we found the Instruction. assert(It != BB.end()); - // We don't want to erase the instruction itself. - It--; // Delete in rev order to ensure no dangling references. while (It != BB.begin()) { auto Prev = std::prev(It); diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 8dc529b4d7a2..cfb495df9d53 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -32,6 +32,8 @@ middle_assert_shl_overflow = middle_assert_shr_overflow = attempt to shift right by `{$val}`, which would overflow +middle_autodiff_unsafe_inner_const_ref = reading from a `Duplicated` const {$ty} is unsafe + middle_bounds_check = index out of bounds: the length is {$len} but the index is {$index} @@ -107,6 +109,8 @@ middle_type_length_limit = reached the type-length limit while instantiating `{$ middle_unknown_layout = the type `{$ty}` has an unknown layout +middle_unsupported_union = we don't support unions yet: '{$ty_name}' + middle_values_too_big = values of the type `{$ty}` are too big for the target architecture diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 750531b638e4..eaccd8c360eb 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -87,6 +87,7 @@ macro_rules! arena_types { [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, [decode] attribute: rustc_hir::Attribute, [] name_set: rustc_data_structures::unord::UnordSet, + [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, [] pats: rustc_middle::ty::PatternKind<'tcx>, diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index b0187a1848cf..b30d3a950c6a 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -37,6 +37,20 @@ pub struct OpaqueHiddenTypeMismatch<'tcx> { pub sub: TypeMismatchReason, } +#[derive(Diagnostic)] +#[diag(middle_unsupported_union)] +pub struct UnsupportedUnion { + pub ty_name: String, +} + +#[derive(Diagnostic)] +#[diag(middle_autodiff_unsafe_inner_const_ref)] +pub struct AutodiffUnsafeInnerConstRef { + #[primary_span] + pub span: Span, + pub ty: String, +} + #[derive(Subdiagnostic)] pub enum TypeMismatchReason { #[label(middle_conflict_types)] diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index cc980f6e62ae..1784665bcae8 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,4 +1,5 @@ use rustc_abi::Align; +use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; @@ -52,6 +53,8 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option, + /// For the `#[autodiff]` macros. + pub autodiff_item: Option, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -160,6 +163,7 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, + autodiff_item: None, } } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 3eccf56d8c4d..b93046ca8e22 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,6 +1,7 @@ use std::fmt; use std::hash::Hash; +use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_attr_parsing::InlineAttr; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; @@ -251,6 +252,7 @@ impl ToStableHashKey> for MonoItem<'_> { pub struct MonoItemPartitions<'tcx> { pub codegen_units: &'tcx [CodegenUnit<'tcx>], pub all_mono_items: &'tcx DefIdSet, + pub autodiff_items: &'tcx [AutoDiffItem], } #[derive(Debug, HashStable)] diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 9bdaeb015cd5..5462105e5e8e 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -15,6 +16,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } serde = "1" serde_json = "1" diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bb603df11294..f3b5488e9751 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -257,7 +257,7 @@ struct SharedState<'tcx> { pub(crate) struct UsageMap<'tcx> { // Maps every mono item to the mono items used by it. - used_map: UnordMap, Vec>>, + pub used_map: UnordMap, Vec>>, // Maps every mono item to the mono items that use it. user_map: UnordMap, Vec>>, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index e08c348a64d7..c985ea04278d 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -92,6 +92,8 @@ //! source-level module, functions from the same module will be available for //! inlining, even when they are not marked `#[inline]`. +mod autodiff; + use std::cmp; use std::collections::hash_map::Entry; use std::fs::{self, File}; @@ -251,7 +253,17 @@ where can_export_generics, always_export_generics, ); - if visibility == Visibility::Hidden && can_be_internalized { + + // We can't differentiate something that got inlined. + let autodiff_active = cfg!(llvm_enzyme) + && cx + .tcx + .codegen_fn_attrs(mono_item.def_id()) + .autodiff_item + .as_ref() + .is_some_and(|ad| ad.is_active()); + + if !autodiff_active && visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); } let size_estimate = mono_item.size_estimate(cx.tcx); @@ -1176,6 +1188,18 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio }) .collect(); + let autodiff_mono_items: Vec<_> = items + .iter() + .filter_map(|item| match *item { + MonoItem::Fn(ref instance) => Some((item, instance)), + _ => None, + }) + .collect(); + + let autodiff_items = + autodiff::find_autodiff_source_functions(tcx, &usage_map, autodiff_mono_items); + let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items); + // Output monomorphization stats per def_id if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats { if let Err(err) = @@ -1236,7 +1260,11 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio } } - MonoItemPartitions { all_mono_items: tcx.arena.alloc(mono_items), codegen_units } + MonoItemPartitions { + all_mono_items: tcx.arena.alloc(mono_items), + codegen_units, + autodiff_items, + } } /// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s diff --git a/compiler/rustc_monomorphize/src/partitioning/autodiff.rs b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs new file mode 100644 index 000000000000..bce31bf0748e --- /dev/null +++ b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs @@ -0,0 +1,121 @@ +use rustc_ast::expand::autodiff_attrs::{AutoDiffItem, DiffActivity}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_symbol_mangling::symbol_name_for_instance_in_crate; +use tracing::{debug, trace}; + +use crate::partitioning::UsageMap; + +fn adjust_activity_to_abi<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>, da: &mut Vec) { + if !matches!(fn_ty.kind(), ty::FnDef(..)) { + bug!("expected fn def for autodiff, got {:?}", fn_ty); + } + let fnc_binder: ty::Binder<'_, ty::FnSig<'_>> = fn_ty.fn_sig(tcx); + + // If rustc compiles the unmodified primal, we know that this copy of the function + // also has correct lifetimes. We know that Enzyme won't free the shadow too early + // (or actually at all), so let's strip lifetimes when computing the layout. + let x = tcx.instantiate_bound_regions_with_erased(fnc_binder); + let mut new_activities = vec![]; + let mut new_positions = vec![]; + for (i, ty) in x.inputs().iter().enumerate() { + if let Some(inner_ty) = ty.builtin_deref(true) { + if ty.is_fn_ptr() { + // FIXME(ZuseZ4): add a nicer error, or just figure out how to support them, + // since Enzyme itself can handle them. + tcx.dcx().err("function pointers are currently not supported in autodiff"); + } + if inner_ty.is_slice() { + // We know that the length will be passed as extra arg. + if !da.is_empty() { + // We are looking at a slice. The length of that slice will become an + // extra integer on llvm level. Integers are always const. + // However, if the slice get's duplicated, we want to know to later check the + // size. So we mark the new size argument as FakeActivitySize. + let activity = match da[i] { + DiffActivity::DualOnly + | DiffActivity::Dual + | DiffActivity::DuplicatedOnly + | DiffActivity::Duplicated => DiffActivity::FakeActivitySize, + DiffActivity::Const => DiffActivity::Const, + _ => bug!("unexpected activity for ptr/ref"), + }; + new_activities.push(activity); + new_positions.push(i + 1); + } + continue; + } + } + } + // now add the extra activities coming from slices + // Reverse order to not invalidate the indices + for _ in 0..new_activities.len() { + let pos = new_positions.pop().unwrap(); + let activity = new_activities.pop().unwrap(); + da.insert(pos, activity); + } +} + +pub(crate) fn find_autodiff_source_functions<'tcx>( + tcx: TyCtxt<'tcx>, + usage_map: &UsageMap<'tcx>, + autodiff_mono_items: Vec<(&MonoItem<'tcx>, &Instance<'tcx>)>, +) -> Vec { + let mut autodiff_items: Vec = vec![]; + for (item, instance) in autodiff_mono_items { + let target_id = instance.def_id(); + let cg_fn_attr = tcx.codegen_fn_attrs(target_id).autodiff_item.clone(); + let Some(target_attrs) = cg_fn_attr else { + continue; + }; + let mut input_activities: Vec = target_attrs.input_activity.clone(); + if target_attrs.is_source() { + trace!("source found: {:?}", target_id); + } + if !target_attrs.apply_autodiff() { + continue; + } + + let target_symbol = symbol_name_for_instance_in_crate(tcx, instance.clone(), LOCAL_CRATE); + + let source = + usage_map.used_map.get(&item).unwrap().into_iter().find_map(|item| match *item { + MonoItem::Fn(ref instance_s) => { + let source_id = instance_s.def_id(); + if let Some(ad) = &tcx.codegen_fn_attrs(source_id).autodiff_item + && ad.is_active() + { + return Some(instance_s); + } + None + } + _ => None, + }); + let inst = match source { + Some(source) => source, + None => continue, + }; + + debug!("source_id: {:?}", inst.def_id()); + let fn_ty = inst.ty(tcx, ty::TypingEnv::fully_monomorphized()); + assert!(fn_ty.is_fn()); + adjust_activity_to_abi(tcx, fn_ty, &mut input_activities); + let symb = symbol_name_for_instance_in_crate(tcx, inst.clone(), LOCAL_CRATE); + + let mut new_target_attrs = target_attrs.clone(); + new_target_attrs.input_activity = input_activities; + let itm = new_target_attrs.into_item(symb, target_symbol); + autodiff_items.push(itm); + } + + if !autodiff_items.is_empty() { + trace!("AUTODIFF ITEMS EXIST"); + for item in &mut *autodiff_items { + trace!("{}", &item); + } + } + + autodiff_items +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 97bd2670aa64..c8a811985d5b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -189,6 +189,39 @@ pub enum CoverageLevel { Mcdc, } +/// The different settings that the `-Z autodiff` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum AutoDiff { + /// Print TypeAnalysis information + PrintTA, + /// Print ActivityAnalysis Information + PrintAA, + /// Print Performance Warnings from Enzyme + PrintPerf, + /// Combines the three print flags above. + Print, + /// Print the whole module, before running opts. + PrintModBefore, + /// Print the whole module just before we pass it to Enzyme. + /// For Debug purpose, prefer the OPT flag below + PrintModAfterOpts, + /// Print the module after Enzyme differentiated everything. + PrintModAfterEnzyme, + + /// Enzyme's loose type debug helper (can cause incorrect gradients) + LooseTypes, + + /// More flags + NoModOptAfter, + /// Tell Enzyme to run LLVM Opts on each function it generated. By default off, + /// since we already optimize the whole module after Enzyme is done. + EnableFncOpt, + NoVecUnroll, + RuntimeActivity, + /// Runs Enzyme specific Inlining + Inline, +} + /// Settings for `-Z instrument-xray` flag. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct InstrumentXRay { @@ -2902,7 +2935,7 @@ pub(crate) mod dep_tracking { }; use super::{ - BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, + AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, OomStrategy, OptLevel, OutFileName, @@ -2950,6 +2983,7 @@ pub(crate) mod dep_tracking { } impl_dep_tracking_hash_via_hash!( + AutoDiff, bool, usize, NonZero, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 63aaa3abc8e5..3b6d1a120181 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -398,6 +398,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Print`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfterOpts`, `PrintModAfterEnzyme`, `LooseTypes`, `NoModOptAfter`, `EnableFncOpt`, `NoVecUnroll`, `Inline`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -1029,6 +1030,38 @@ pub mod parse { } } + pub(crate) fn parse_autodiff(slot: &mut Vec, v: Option<&str>) -> bool { + let Some(v) = v else { + *slot = vec![]; + return true; + }; + let mut v: Vec<&str> = v.split(",").collect(); + v.sort_unstable(); + for &val in v.iter() { + let variant = match val { + "PrintTA" => AutoDiff::PrintTA, + "PrintAA" => AutoDiff::PrintAA, + "PrintPerf" => AutoDiff::PrintPerf, + "Print" => AutoDiff::Print, + "PrintModBefore" => AutoDiff::PrintModBefore, + "PrintModAfterOpts" => AutoDiff::PrintModAfterOpts, + "PrintModAfterEnzyme" => AutoDiff::PrintModAfterEnzyme, + "LooseTypes" => AutoDiff::LooseTypes, + "NoModOptAfter" => AutoDiff::NoModOptAfter, + "EnableFncOpt" => AutoDiff::EnableFncOpt, + "NoVecUnroll" => AutoDiff::NoVecUnroll, + "Inline" => AutoDiff::Inline, + _ => { + // FIXME(ZuseZ4): print an error saying which value is not recognized + return false; + } + }; + slot.push(variant); + } + + true + } + pub(crate) fn parse_instrument_coverage( slot: &mut InstrumentCoverage, v: Option<&str>, @@ -1736,6 +1769,22 @@ options! { either `loaded` or `not-loaded`."), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + autodiff: Vec = (Vec::new(), parse_autodiff, [TRACKED], + "a list of optional autodiff flags to enable + Optional extra settings: + `=PrintTA` + `=PrintAA` + `=PrintPerf` + `=Print` + `=PrintModBefore` + `=PrintModAfterOpts` + `=PrintModAfterEnzyme` + `=LooseTypes` + `=NoModOptAfter` + `=EnableFncOpt` + `=NoVecUnroll` + `=Inline` + Multiple options can be combined with commas."), #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1fb15fe98000..cf9f2148c24c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -502,7 +502,6 @@ symbols! { augmented_assignments, auto_traits, autodiff, - autodiff_fallback, automatically_derived, avx, avx512_target_feature, @@ -568,7 +567,6 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, - cfg_autodiff_fallback, cfg_boolean_literals, cfg_doctest, cfg_emscripten_wasm_eh, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index fd9bf47234c6..f447d186a524 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1049,9 +1049,12 @@ pub fn rustc_cargo( // . cargo.rustflag("-Zon-broken-pipe=kill"); - if builder.config.llvm_enzyme { - cargo.rustflag("-l").rustflag("Enzyme-19"); - } + // We temporarily disable linking here as part of some refactoring. + // This way, people can manually use -Z llvm-plugins and -C passes=enzyme for now. + // In a follow-up PR, we will re-enable linking here and load the pass for them. + //if builder.config.llvm_enzyme { + // cargo.rustflag("-l").rustflag("Enzyme-19"); + //} // Building with protected visibility reduces the number of dynamic relocations needed, giving // us a faster startup time. However GNU ld < 2.40 will error if we try to link a shared object diff --git a/src/doc/unstable-book/src/compiler-flags/autodiff.md b/src/doc/unstable-book/src/compiler-flags/autodiff.md new file mode 100644 index 000000000000..4e55be0ad95a --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/autodiff.md @@ -0,0 +1,23 @@ +# `autodiff` + +The tracking issue for this feature is: [#124509](https://github.com/rust-lang/rust/issues/124509). + +------------------------ + +This feature allows you to differentiate functions using automatic differentiation. +Set the `-Zautodiff=` compiler flag to adjust the behaviour of the autodiff feature. +Multiple options can be separated with a comma. Valid options are: + +`PrintTA` - print Type Analysis Information +`PrintAA` - print Activity Analysis Information +`PrintPerf` - print Performance Warnings from Enzyme +`Print` - prints all intermediate transformations +`PrintModBefore` - print the whole module, before running opts +`PrintModAfterOpts` - print the whole module just before we pass it to Enzyme +`PrintModAfterEnzyme` - print the module after Enzyme differentiated everything +`LooseTypes` - Enzyme's loose type debug helper (can cause incorrect gradients) +`Inline` - runs Enzyme specific Inlining +`NoModOptAfter` - do not optimize the module after Enzyme is done +`EnableFncOpt` - tell Enzyme to run LLVM Opts on each function it generated +`NoVecUnroll` - do not unroll vectorized loops +`RuntimeActivity` - allow specifying activity at runtime diff --git a/src/tools/enzyme b/src/tools/enzyme index 2fe5164a2423..0e5fa4a3d475 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 2fe5164a2423dd67ef25e2c4fb204fd06362494b +Subproject commit 0e5fa4a3d475f4dece489c9e06b11164f83789f5 From 51eaa0d56aadeea9eb3a3c011189d20bf9333bc7 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Sun, 19 Jan 2025 13:04:28 -0600 Subject: [PATCH 266/342] Clean up uses of the unstable `dwarf_version` option - Consolidate calculation of the effective value. - Check the target `DebuginfoKind` instead of using `is_like_msvc`. --- .../src/debuginfo/metadata.rs | 3 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 49 ++++++++++--------- compiler/rustc_session/src/options.rs | 1 + compiler/rustc_session/src/session.rs | 8 ++- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 8d782a618fc0..2a1a12f3ce59 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -919,8 +919,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( .unwrap_or_default(); let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo); - let dwarf_version = - tcx.sess.opts.unstable_opts.dwarf_version.unwrap_or(tcx.sess.target.default_dwarf_version); + let dwarf_version = tcx.sess.dwarf_version(); let is_dwarf_kind = matches!(tcx.sess.target.debuginfo_kind, DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym); // Don't emit `.debug_pubnames` and `.debug_pubtypes` on DWARFv4 or lower. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index e6778411365f..5089560784a0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -22,6 +22,7 @@ use rustc_session::config::{self, DebugInfo}; use rustc_span::{ BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol, }; +use rustc_target::spec::DebuginfoKind; use smallvec::SmallVec; use tracing::debug; @@ -93,29 +94,31 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { pub(crate) fn finalize(&self, sess: &Session) { unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) }; - if !sess.target.is_like_msvc { - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than macOS currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for macOS to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - let dwarf_version = - sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); - llvm::add_module_flag_u32( - self.llmod, - llvm::ModuleFlagMergeBehavior::Warning, - "Dwarf Version", - dwarf_version, - ); - } else { - // Indicate that we want CodeView debug information on MSVC - llvm::add_module_flag_u32( - self.llmod, - llvm::ModuleFlagMergeBehavior::Warning, - "CodeView", - 1, - ); + + match sess.target.debuginfo_kind { + DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Warning, + "Dwarf Version", + sess.dwarf_version(), + ); + } + DebuginfoKind::Pdb => { + // Indicate that we want CodeView debug information + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Warning, + "CodeView", + 1, + ); + } } // Prevent bitcode readers from deleting the debug info. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4ce638251297..c7af40a8ad4e 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1803,6 +1803,7 @@ options! { "output statistics about monomorphization collection"), dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED], "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"), + #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] dwarf_version: Option = (None, parse_opt_number, [TRACKED], "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1f03de3f53db..c0f5f0d4a9e9 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -732,6 +732,11 @@ impl Session { self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) } + /// Returns the DWARF version passed on the CLI or the default for the target. + pub fn dwarf_version(&self) -> u32 { + self.opts.unstable_opts.dwarf_version.unwrap_or(self.target.default_dwarf_version) + } + pub fn stack_protector(&self) -> StackProtector { if self.target.options.supports_stack_protector { self.opts.unstable_opts.stack_protector @@ -1263,8 +1268,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } if sess.opts.unstable_opts.embed_source { - let dwarf_version = - sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); + let dwarf_version = sess.dwarf_version(); if dwarf_version < 5 { sess.dcx().emit_warn(errors::EmbedSourceInsufficientDwarfVersion { dwarf_version }); From 4d5a63f8825e127040a078d3a81d7904a7cfb1ad Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Sun, 19 Jan 2025 13:22:25 -0600 Subject: [PATCH 267/342] Add tracking issue to docs --- src/doc/unstable-book/src/compiler-flags/dwarf-version.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/unstable-book/src/compiler-flags/dwarf-version.md b/src/doc/unstable-book/src/compiler-flags/dwarf-version.md index c5e86f17df74..e88799d2cf04 100644 --- a/src/doc/unstable-book/src/compiler-flags/dwarf-version.md +++ b/src/doc/unstable-book/src/compiler-flags/dwarf-version.md @@ -1,5 +1,9 @@ ## `dwarf-version` +The tracking issue for this feature is: + +---------------------------- + This option controls the version of DWARF that the compiler emits, on platforms that use DWARF to encode debug information. It takes one of the following values: From d78193381249740d769970d4ec453c5bc265f082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 28 Jan 2025 08:53:19 +0000 Subject: [PATCH 268/342] add constraint graph to polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 110 +++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index f71e6f3e6f3a..6d32ee17f4c2 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,17 +1,19 @@ use std::io; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; -use rustc_middle::mir::{Body, ClosureRegionRequirements}; +use rustc_middle::mir::{Body, ClosureRegionRequirements, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_mir_dataflow::points::PointIndex; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; @@ -80,14 +82,27 @@ fn emit_polonius_dump<'tcx>( body, regioncx, borrow_set, - localized_outlives_constraints, + &localized_outlives_constraints, closure_region_requirements, out, )?; writeln!(out, "")?; writeln!(out, "")?; - // Section 2: mermaid visualization of the CFG. + // Section 2: mermaid visualization of the polonius constraint graph. + writeln!(out, "
")?; + writeln!(out, "Polonius constraint graph")?; + writeln!(out, "
")?;
+    let edge_count = emit_mermaid_constraint_graph(
+        borrow_set,
+        regioncx.liveness_constraints(),
+        &localized_outlives_constraints,
+        out,
+    )?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 3: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; writeln!(out, "
")?;
@@ -95,7 +110,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "
")?; writeln!(out, "
")?; - // Section 3: mermaid visualization of the NLL region graph. + // Section 4: mermaid visualization of the NLL region graph. writeln!(out, "
")?; writeln!(out, "NLL regions")?; writeln!(out, "
")?;
@@ -103,7 +118,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "
")?; writeln!(out, "
")?; - // Section 4: mermaid visualization of the NLL SCC graph. + // Section 5: mermaid visualization of the NLL SCC graph. writeln!(out, "
")?; writeln!(out, "NLL SCCs")?; writeln!(out, "
")?;
@@ -117,7 +132,11 @@ fn emit_polonius_dump<'tcx>(
         ""
     )?;
     writeln!(out, "")?;
     writeln!(out, "")?;
@@ -132,7 +151,7 @@ fn emit_html_mir<'tcx>(
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
-    localized_outlives_constraints: LocalizedOutlivesConstraintSet,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
     closure_region_requirements: &Option>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
@@ -160,7 +179,7 @@ fn emit_html_mir<'tcx>(
                 regioncx,
                 closure_region_requirements,
                 borrow_set,
-                &localized_outlives_constraints,
+                localized_outlives_constraints,
                 pass_where,
                 out,
             )
@@ -392,3 +411,76 @@ fn emit_mermaid_nll_sccs<'tcx>(
 
     Ok(())
 }
+
+/// Emits a mermaid flowchart of the polonius localized outlives constraints, with subgraphs per
+/// region, and loan introductions.
+fn emit_mermaid_constraint_graph<'tcx>(
+    borrow_set: &BorrowSet<'tcx>,
+    liveness: &LivenessValues,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
+    out: &mut dyn io::Write,
+) -> io::Result {
+    let location_name = |location: Location| {
+        // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id,
+        // transform it into `BB5_2`.
+        format!("BB{}_{}", location.block.index(), location.statement_index)
+    };
+    let region_name = |region: RegionVid| format!("'{}", region.index());
+    let node_name = |region: RegionVid, point: PointIndex| {
+        let location = liveness.location_from_point(point);
+        format!("{}_{}", region_name(region), location_name(location))
+    };
+
+    // The mermaid chart type: a top-down flowchart, which supports subgraphs.
+    writeln!(out, "flowchart TD")?;
+
+    // The loans subgraph: a node per loan.
+    writeln!(out, "    subgraph \"Loans\"")?;
+    for loan_idx in 0..borrow_set.len() {
+        writeln!(out, "        L{loan_idx}")?;
+    }
+    writeln!(out, "    end\n")?;
+
+    // And an edge from that loan node to where it enters the constraint graph.
+    for (loan_idx, loan) in borrow_set.iter_enumerated() {
+        writeln!(
+            out,
+            "    L{} --> {}_{}",
+            loan_idx.index(),
+            region_name(loan.region),
+            location_name(loan.reserve_location),
+        )?;
+    }
+    writeln!(out, "")?;
+
+    // The regions subgraphs containing the region/point nodes.
+    let mut points_per_region: FxIndexMap> =
+        FxIndexMap::default();
+    for constraint in &localized_outlives_constraints.outlives {
+        points_per_region.entry(constraint.source).or_default().insert(constraint.from);
+        points_per_region.entry(constraint.target).or_default().insert(constraint.to);
+    }
+    for (region, points) in points_per_region {
+        writeln!(out, "    subgraph \"{}\"", region_name(region))?;
+        for point in points {
+            writeln!(out, "        {}", node_name(region, point))?;
+        }
+        writeln!(out, "    end\n")?;
+    }
+
+    // The constraint graph edges.
+    for constraint in &localized_outlives_constraints.outlives {
+        // FIXME: add killed loans and constraint kind as edge labels.
+        writeln!(
+            out,
+            "    {} --> {}",
+            node_name(constraint.source, constraint.from),
+            node_name(constraint.target, constraint.to),
+        )?;
+    }
+
+    // Return the number of edges: this is the biggest graph in the dump and its edge count will be
+    // mermaid's max edge count to support.
+    let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len();
+    Ok(edge_count)
+}

From 851322b74db9ac91a1b9d206c5f80fc51a97f7c1 Mon Sep 17 00:00:00 2001
From: Bastian Kersting 
Date: Mon, 16 Dec 2024 14:42:49 +0000
Subject: [PATCH 269/342] Refactor `PointerFinder` into a separate module

This also parameterize the "excluded pointee types" and exposes a
general method for inserting checks on pointers.

This is a preparation for adding a NullCheck that makes use of the same
code.
---
 .../src/check_alignment.rs                    | 196 +++------------
 .../rustc_mir_transform/src/check_pointers.rs | 233 ++++++++++++++++++
 compiler/rustc_mir_transform/src/lib.rs       |   1 +
 3 files changed, 271 insertions(+), 159 deletions(-)
 create mode 100644 compiler/rustc_mir_transform/src/check_pointers.rs

diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index d7e22c123942..ca5564e447ae 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -1,11 +1,10 @@
-use rustc_hir::lang_items::LangItem;
 use rustc_index::IndexVec;
 use rustc_middle::mir::interpret::Scalar;
-use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_session::Session;
-use tracing::{debug, trace};
+
+use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
 
 pub(super) struct CheckAlignment;
 
@@ -19,46 +18,19 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        // This pass emits new panics. If for whatever reason we do not have a panic
-        // implementation, running this pass may cause otherwise-valid code to not compile.
-        if tcx.lang_items().get(LangItem::PanicImpl).is_none() {
-            return;
-        }
+        // Skip trivially aligned place types.
+        let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8];
 
-        let typing_env = body.typing_env(tcx);
-        let basic_blocks = body.basic_blocks.as_mut();
-        let local_decls = &mut body.local_decls;
-
-        // This pass inserts new blocks. Each insertion changes the Location for all
-        // statements/blocks after. Iterating or visiting the MIR in order would require updating
-        // our current location after every insertion. By iterating backwards, we dodge this issue:
-        // The only Locations that an insertion changes have already been handled.
-        for block in (0..basic_blocks.len()).rev() {
-            let block = block.into();
-            for statement_index in (0..basic_blocks[block].statements.len()).rev() {
-                let location = Location { block, statement_index };
-                let statement = &basic_blocks[block].statements[statement_index];
-                let source_info = statement.source_info;
-
-                let mut finder =
-                    PointerFinder { tcx, local_decls, typing_env, pointers: Vec::new() };
-                finder.visit_statement(statement, location);
-
-                for (local, ty) in finder.pointers {
-                    debug!("Inserting alignment check for {:?}", ty);
-                    let new_block = split_block(basic_blocks, location);
-                    insert_alignment_check(
-                        tcx,
-                        local_decls,
-                        &mut basic_blocks[block],
-                        local,
-                        ty,
-                        source_info,
-                        new_block,
-                    );
-                }
-            }
-        }
+        // We have to exclude borrows here: in `&x.field`, the exact
+        // requirement is that the final reference must be aligned, but
+        // `check_pointers` would check that `x` is aligned, which would be wrong.
+        check_pointers(
+            tcx,
+            body,
+            &excluded_pointees,
+            insert_alignment_check,
+            BorrowCheckMode::ExcludeBorrows,
+        );
     }
 
     fn is_required(&self) -> bool {
@@ -66,119 +38,33 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
     }
 }
 
-struct PointerFinder<'a, 'tcx> {
-    tcx: TyCtxt<'tcx>,
-    local_decls: &'a mut LocalDecls<'tcx>,
-    typing_env: ty::TypingEnv<'tcx>,
-    pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
-    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
-        // We want to only check reads and writes to Places, so we specifically exclude
-        // Borrow and RawBorrow.
-        match context {
-            PlaceContext::MutatingUse(
-                MutatingUseContext::Store
-                | MutatingUseContext::AsmOutput
-                | MutatingUseContext::Call
-                | MutatingUseContext::Yield
-                | MutatingUseContext::Drop,
-            ) => {}
-            PlaceContext::NonMutatingUse(
-                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
-            ) => {}
-            _ => {
-                return;
-            }
-        }
-
-        if !place.is_indirect() {
-            return;
-        }
-
-        // Since Deref projections must come first and only once, the pointer for an indirect place
-        // is the Local that the Place is based on.
-        let pointer = Place::from(place.local);
-        let pointer_ty = self.local_decls[place.local].ty;
-
-        // We only want to check places based on unsafe pointers
-        if !pointer_ty.is_unsafe_ptr() {
-            trace!("Indirect, but not based on an unsafe ptr, not checking {:?}", place);
-            return;
-        }
-
-        let pointee_ty =
-            pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer");
-        // Ideally we'd support this in the future, but for now we are limited to sized types.
-        if !pointee_ty.is_sized(self.tcx, self.typing_env) {
-            debug!("Unsafe pointer, but pointee is not known to be sized: {:?}", pointer_ty);
-            return;
-        }
-
-        // Try to detect types we are sure have an alignment of 1 and skip the check
-        // We don't need to look for str and slices, we already rejected unsized types above
-        let element_ty = match pointee_ty.kind() {
-            ty::Array(ty, _) => *ty,
-            _ => pointee_ty,
-        };
-        if [self.tcx.types.bool, self.tcx.types.i8, self.tcx.types.u8].contains(&element_ty) {
-            debug!("Trivially aligned place type: {:?}", pointee_ty);
-            return;
-        }
-
-        // Ensure that this place is based on an aligned pointer.
-        self.pointers.push((pointer, pointee_ty));
-
-        self.super_place(place, context, location);
-    }
-}
-
-fn split_block(
-    basic_blocks: &mut IndexVec>,
-    location: Location,
-) -> BasicBlock {
-    let block_data = &mut basic_blocks[location.block];
-
-    // Drain every statement after this one and move the current terminator to a new basic block
-    let new_block = BasicBlockData {
-        statements: block_data.statements.split_off(location.statement_index),
-        terminator: block_data.terminator.take(),
-        is_cleanup: block_data.is_cleanup,
-    };
-
-    basic_blocks.push(new_block)
-}
-
+/// Inserts the actual alignment check's logic. Returns a
+/// [AssertKind::MisalignedPointerDereference] on failure.
 fn insert_alignment_check<'tcx>(
     tcx: TyCtxt<'tcx>,
-    local_decls: &mut IndexVec>,
-    block_data: &mut BasicBlockData<'tcx>,
     pointer: Place<'tcx>,
     pointee_ty: Ty<'tcx>,
+    local_decls: &mut IndexVec>,
+    stmts: &mut Vec>,
     source_info: SourceInfo,
-    new_block: BasicBlock,
-) {
-    // Cast the pointer to a *const ()
+) -> PointerCheck<'tcx> {
+    // Cast the pointer to a *const ().
     let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
     let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
     let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
-    block_data
-        .statements
+    stmts
         .push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) });
 
-    // Transmute the pointer to a usize (equivalent to `ptr.addr()`)
+    // Transmute the pointer to a usize (equivalent to `ptr.addr()`).
     let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize);
     let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
-    block_data
-        .statements
-        .push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });
+    stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });
 
     // Get the alignment of the pointee
     let alignment =
         local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
     let rvalue = Rvalue::NullaryOp(NullOp::AlignOf, pointee_ty);
-    block_data.statements.push(Statement {
+    stmts.push(Statement {
         source_info,
         kind: StatementKind::Assign(Box::new((alignment, rvalue))),
     });
@@ -191,7 +77,7 @@ fn insert_alignment_check<'tcx>(
         user_ty: None,
         const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)), tcx.types.usize),
     }));
-    block_data.statements.push(Statement {
+    stmts.push(Statement {
         source_info,
         kind: StatementKind::Assign(Box::new((
             alignment_mask,
@@ -202,7 +88,7 @@ fn insert_alignment_check<'tcx>(
     // BitAnd the alignment mask with the pointer
     let alignment_bits =
         local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
-    block_data.statements.push(Statement {
+    stmts.push(Statement {
         source_info,
         kind: StatementKind::Assign(Box::new((
             alignment_bits,
@@ -220,7 +106,7 @@ fn insert_alignment_check<'tcx>(
         user_ty: None,
         const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize),
     }));
-    block_data.statements.push(Statement {
+    stmts.push(Statement {
         source_info,
         kind: StatementKind::Assign(Box::new((
             is_ok,
@@ -228,21 +114,13 @@ fn insert_alignment_check<'tcx>(
         ))),
     });
 
-    // Set this block's terminator to our assert, continuing to new_block if we pass
-    block_data.terminator = Some(Terminator {
-        source_info,
-        kind: TerminatorKind::Assert {
-            cond: Operand::Copy(is_ok),
-            expected: true,
-            target: new_block,
-            msg: Box::new(AssertKind::MisalignedPointerDereference {
-                required: Operand::Copy(alignment),
-                found: Operand::Copy(addr),
-            }),
-            // This calls panic_misaligned_pointer_dereference, which is #[rustc_nounwind].
-            // We never want to insert an unwind into unsafe code, because unwinding could
-            // make a failing UB check turn into much worse UB when we start unwinding.
-            unwind: UnwindAction::Unreachable,
-        },
-    });
+    // Emit a check that asserts on the alignment and otherwise triggers a
+    // AssertKind::MisalignedPointerDereference.
+    PointerCheck {
+        cond: Operand::Copy(is_ok),
+        assert_kind: Box::new(AssertKind::MisalignedPointerDereference {
+            required: Operand::Copy(alignment),
+            found: Operand::Copy(addr),
+        }),
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs
new file mode 100644
index 000000000000..95d3ce05000e
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/check_pointers.rs
@@ -0,0 +1,233 @@
+use rustc_hir::lang_items::LangItem;
+use rustc_index::IndexVec;
+use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::*;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use tracing::{debug, trace};
+
+/// Details of a pointer check, the condition on which we decide whether to
+/// fail the assert and an [AssertKind] that defines the behavior on failure.
+pub(crate) struct PointerCheck<'tcx> {
+    pub(crate) cond: Operand<'tcx>,
+    pub(crate) assert_kind: Box>>,
+}
+
+/// Indicates whether we insert the checks for borrow places of a raw pointer.
+/// Concretely places with [MutatingUseContext::Borrow] or
+/// [NonMutatingUseContext::SharedBorrow].
+#[derive(Copy, Clone)]
+pub(crate) enum BorrowCheckMode {
+    ExcludeBorrows,
+}
+
+/// Utility for adding a check for read/write on every sized, raw pointer.
+///
+/// Visits every read/write access to a [Sized], raw pointer and inserts a
+/// new basic block directly before the pointer access. (Read/write accesses
+/// are determined by the `PlaceContext` of the MIR visitor.) Then calls
+/// `on_finding` to insert the actual logic for a pointer check (e.g. check for
+/// alignment). A check can choose to be inserted for (mutable) borrows of
+/// raw pointers via the `borrow_check_mode` parameter.
+///
+/// This utility takes care of the right order of blocks, the only thing a
+/// caller must do in `on_finding` is:
+/// - Append [Statement]s to `stmts`.
+/// - Append [LocalDecl]s to `local_decls`.
+/// - Return a [PointerCheck] that contains the condition and an [AssertKind].
+///   The AssertKind must be a panic with `#[rustc_nounwind]`. The condition
+///   should always return the boolean `is_ok`, so evaluate to true in case of
+///   success and fail the check otherwise.
+/// This utility will insert a terminator block that asserts on the condition
+/// and panics on failure.
+pub(crate) fn check_pointers<'a, 'tcx, F>(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    excluded_pointees: &'a [Ty<'tcx>],
+    on_finding: F,
+    borrow_check_mode: BorrowCheckMode,
+) where
+    F: Fn(
+        /* tcx: */ TyCtxt<'tcx>,
+        /* pointer: */ Place<'tcx>,
+        /* pointee_ty: */ Ty<'tcx>,
+        /* local_decls: */ &mut IndexVec>,
+        /* stmts: */ &mut Vec>,
+        /* source_info: */ SourceInfo,
+    ) -> PointerCheck<'tcx>,
+{
+    // This pass emits new panics. If for whatever reason we do not have a panic
+    // implementation, running this pass may cause otherwise-valid code to not compile.
+    if tcx.lang_items().get(LangItem::PanicImpl).is_none() {
+        return;
+    }
+
+    let typing_env = body.typing_env(tcx);
+    let basic_blocks = body.basic_blocks.as_mut();
+    let local_decls = &mut body.local_decls;
+
+    // This operation inserts new blocks. Each insertion changes the Location for all
+    // statements/blocks after. Iterating or visiting the MIR in order would require updating
+    // our current location after every insertion. By iterating backwards, we dodge this issue:
+    // The only Locations that an insertion changes have already been handled.
+    for block in (0..basic_blocks.len()).rev() {
+        let block = block.into();
+        for statement_index in (0..basic_blocks[block].statements.len()).rev() {
+            let location = Location { block, statement_index };
+            let statement = &basic_blocks[block].statements[statement_index];
+            let source_info = statement.source_info;
+
+            let mut finder = PointerFinder::new(
+                tcx,
+                local_decls,
+                typing_env,
+                excluded_pointees,
+                borrow_check_mode,
+            );
+            finder.visit_statement(statement, location);
+
+            for (local, ty) in finder.into_found_pointers() {
+                debug!("Inserting check for {:?}", ty);
+                let new_block = split_block(basic_blocks, location);
+
+                // Invoke `on_finding` which appends to `local_decls` and the
+                // blocks statements. It returns information about the assert
+                // we're performing in the Terminator.
+                let block_data = &mut basic_blocks[block];
+                let pointer_check = on_finding(
+                    tcx,
+                    local,
+                    ty,
+                    local_decls,
+                    &mut block_data.statements,
+                    source_info,
+                );
+                block_data.terminator = Some(Terminator {
+                    source_info,
+                    kind: TerminatorKind::Assert {
+                        cond: pointer_check.cond,
+                        expected: true,
+                        target: new_block,
+                        msg: pointer_check.assert_kind,
+                        // This calls a panic function associated with the pointer check, which
+                        // is #[rustc_nounwind]. We never want to insert an unwind into unsafe
+                        // code, because unwinding could make a failing UB check turn into much
+                        // worse UB when we start unwinding.
+                        unwind: UnwindAction::Unreachable,
+                    },
+                });
+            }
+        }
+    }
+}
+
+struct PointerFinder<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    local_decls: &'a mut LocalDecls<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
+    pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
+    excluded_pointees: &'a [Ty<'tcx>],
+    borrow_check_mode: BorrowCheckMode,
+}
+
+impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
+    fn new(
+        tcx: TyCtxt<'tcx>,
+        local_decls: &'a mut LocalDecls<'tcx>,
+        typing_env: ty::TypingEnv<'tcx>,
+        excluded_pointees: &'a [Ty<'tcx>],
+        borrow_check_mode: BorrowCheckMode,
+    ) -> Self {
+        PointerFinder {
+            tcx,
+            local_decls,
+            typing_env,
+            excluded_pointees,
+            pointers: Vec::new(),
+            borrow_check_mode,
+        }
+    }
+
+    fn into_found_pointers(self) -> Vec<(Place<'tcx>, Ty<'tcx>)> {
+        self.pointers
+    }
+
+    /// Whether or not we should visit a [Place] with [PlaceContext].
+    ///
+    /// We generally only visit Reads/Writes to a place and only Borrows if
+    /// requested.
+    fn should_visit_place(&self, context: PlaceContext) -> bool {
+        match context {
+            PlaceContext::MutatingUse(
+                MutatingUseContext::Store
+                | MutatingUseContext::Call
+                | MutatingUseContext::Yield
+                | MutatingUseContext::Drop,
+            ) => true,
+            PlaceContext::NonMutatingUse(
+                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
+            ) => true,
+            PlaceContext::MutatingUse(MutatingUseContext::Borrow)
+            | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
+                !matches!(self.borrow_check_mode, BorrowCheckMode::ExcludeBorrows)
+            }
+            _ => false,
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
+    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+        if !self.should_visit_place(context) || !place.is_indirect() {
+            return;
+        }
+
+        // Since Deref projections must come first and only once, the pointer for an indirect place
+        // is the Local that the Place is based on.
+        let pointer = Place::from(place.local);
+        let pointer_ty = self.local_decls[place.local].ty;
+
+        // We only want to check places based on raw pointers
+        if !pointer_ty.is_unsafe_ptr() {
+            trace!("Indirect, but not based on an raw ptr, not checking {:?}", place);
+            return;
+        }
+
+        let pointee_ty =
+            pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer");
+        // Ideally we'd support this in the future, but for now we are limited to sized types.
+        if !pointee_ty.is_sized(self.tcx, self.typing_env) {
+            trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty);
+            return;
+        }
+
+        // We don't need to look for slices, we already rejected unsized types above.
+        let element_ty = match pointee_ty.kind() {
+            ty::Array(ty, _) => *ty,
+            _ => pointee_ty,
+        };
+        if self.excluded_pointees.contains(&element_ty) {
+            trace!("Skipping pointer for type: {:?}", pointee_ty);
+            return;
+        }
+
+        self.pointers.push((pointer, pointee_ty));
+
+        self.super_place(place, context, location);
+    }
+}
+
+fn split_block(
+    basic_blocks: &mut IndexVec>,
+    location: Location,
+) -> BasicBlock {
+    let block_data = &mut basic_blocks[location.block];
+
+    // Drain every statement after this one and move the current terminator to a new basic block.
+    let new_block = BasicBlockData {
+        statements: block_data.statements.split_off(location.statement_index),
+        terminator: block_data.terminator.take(),
+        is_cleanup: block_data.is_cleanup,
+    };
+
+    basic_blocks.push(new_block)
+}
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 2dc55e3614e6..0df9ce0c3d3f 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -44,6 +44,7 @@ use std::sync::LazyLock;
 
 use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel};
 
+mod check_pointers;
 mod cost_checker;
 mod cross_crate_inline;
 mod deduce_param_attrs;

From 23fb08bb532ed8a9b4947c213ae3fc0862b2b254 Mon Sep 17 00:00:00 2001
From: Hans Wennborg 
Date: Thu, 30 Jan 2025 11:18:36 +0100
Subject: [PATCH 270/342] LLVM changed the nocapture attribute to
 captures(none)

This updates RustWrapper.cpp and tests after
https://github.com/llvm/llvm-project/pull/123181
---
 .../rustc_llvm/llvm-wrapper/RustWrapper.cpp   | 11 ++++++++++
 tests/codegen/addr-of-mutate.rs               |  6 +++---
 tests/codegen/cast-target-abi.rs              |  2 +-
 tests/codegen/function-arguments.rs           |  4 ++--
 tests/codegen/i128-x86-align.rs               | 21 ++++++++++++-------
 .../loongarch-abi/loongarch64-lp64d-abi.rs    |  4 ++--
 tests/codegen/packed.rs                       |  4 ++--
 tests/codegen/riscv-abi/riscv64-lp64d-abi.rs  |  2 +-
 8 files changed, 36 insertions(+), 18 deletions(-)

diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 35186778671b..858251ff9c8c 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -319,7 +319,11 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) {
   case LLVMRustAttributeKind::NoAlias:
     return Attribute::NoAlias;
   case LLVMRustAttributeKind::NoCapture:
+#if LLVM_VERSION_GE(21, 0)
+    report_fatal_error("NoCapture doesn't exist in LLVM 21");
+#else
     return Attribute::NoCapture;
+#endif
   case LLVMRustAttributeKind::NoCfCheck:
     return Attribute::NoCfCheck;
   case LLVMRustAttributeKind::NoInline:
@@ -431,6 +435,13 @@ extern "C" void LLVMRustEraseInstFromParent(LLVMValueRef Instr) {
 
 extern "C" LLVMAttributeRef
 LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
+#if LLVM_VERSION_GE(21, 0)
+  // LLVM 21 replaced the NoCapture attribute with Captures(none).
+  if (RustAttr == LLVMRustAttributeKind::NoCapture) {
+    return wrap(Attribute::get(*unwrap(C), Attribute::Captures,
+                               CaptureInfo::none().toIntValue()));
+  }
+#endif
   return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));
 }
 
diff --git a/tests/codegen/addr-of-mutate.rs b/tests/codegen/addr-of-mutate.rs
index f10f01274b1f..14bc4b8ab28c 100644
--- a/tests/codegen/addr-of-mutate.rs
+++ b/tests/codegen/addr-of-mutate.rs
@@ -5,7 +5,7 @@
 // Test for the absence of `readonly` on the argument when it is mutated via `&raw const`.
 // See .
 
-// CHECK: i8 @foo(ptr noalias nocapture noundef align 1 dereferenceable(128) %x)
+// CHECK: i8 @foo(ptr noalias{{( nocapture)?}} noundef align 1{{( captures\(none\))?}} dereferenceable(128) %x)
 #[no_mangle]
 pub fn foo(x: [u8; 128]) -> u8 {
     let ptr = core::ptr::addr_of!(x).cast_mut();
@@ -15,7 +15,7 @@ pub fn foo(x: [u8; 128]) -> u8 {
     x[0]
 }
 
-// CHECK: i1 @second(ptr noalias nocapture noundef align {{[0-9]+}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
+// CHECK: i1 @second(ptr noalias{{( nocapture)?}} noundef align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
 #[no_mangle]
 pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
     let b_bool_ptr = core::ptr::addr_of!(a_ptr_and_b.1.1).cast_mut();
@@ -24,7 +24,7 @@ pub unsafe fn second(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
 }
 
 // If going through a deref (and there are no other mutating accesses), then `readonly` is fine.
-// CHECK: i1 @third(ptr noalias nocapture noundef readonly align {{[0-9]+}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
+// CHECK: i1 @third(ptr noalias{{( nocapture)?}} noundef readonly align {{[0-9]+}}{{( captures\(none\))?}} dereferenceable({{[0-9]+}}) %a_ptr_and_b)
 #[no_mangle]
 pub unsafe fn third(a_ptr_and_b: (*mut (i32, bool), (i64, bool))) -> bool {
     let b_bool_ptr = core::ptr::addr_of!((*a_ptr_and_b.0).1).cast_mut();
diff --git a/tests/codegen/cast-target-abi.rs b/tests/codegen/cast-target-abi.rs
index db76aae3dd00..b3a35cbf3b1c 100644
--- a/tests/codegen/cast-target-abi.rs
+++ b/tests/codegen/cast-target-abi.rs
@@ -392,7 +392,7 @@ pub fn call_fiveu16s() {
 }
 
 // CHECK-LABEL: @return_fiveu16s
-// CHECK-SAME: (ptr {{.+}} sret([10 x i8]) align [[RUST_ALIGN:2]] dereferenceable(10) [[RET_PTR:%.+]])
+// CHECK-SAME: (ptr {{.+}} sret([10 x i8]) align [[RUST_ALIGN:2]] {{.*}}dereferenceable(10) [[RET_PTR:%.+]])
 #[no_mangle]
 pub fn return_fiveu16s() -> FiveU16s {
     // powerpc returns this struct via sret pointer, it doesn't use the cast ABI.
diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs
index 503799d3ed22..1a211dfe096b 100644
--- a/tests/codegen/function-arguments.rs
+++ b/tests/codegen/function-arguments.rs
@@ -135,7 +135,7 @@ pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {}
 #[no_mangle]
 pub fn notunpin_borrow(_: &NotUnpin) {}
 
-// CHECK: @indirect_struct(ptr noalias nocapture noundef readonly align 4 dereferenceable(32) %_1)
+// CHECK: @indirect_struct(ptr noalias{{( nocapture)?}} noundef readonly align 4{{( captures\(none\))?}} dereferenceable(32) %_1)
 #[no_mangle]
 pub fn indirect_struct(_: S) {}
 
@@ -198,7 +198,7 @@ pub fn notunpin_box(x: Box) -> Box {
     x
 }
 
-// CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias nocapture noundef{{( writable)?}} sret([32 x i8]) align 4 dereferenceable(32){{( %_0)?}})
+// CHECK: @struct_return(ptr{{( dead_on_unwind)?}} noalias{{( nocapture)?}} noundef{{( writable)?}} sret([32 x i8]) align 4{{( captures\(none\))?}} dereferenceable(32){{( %_0)?}})
 #[no_mangle]
 pub fn struct_return() -> S {
     S { _field: [0, 0, 0, 0, 0, 0, 0, 0] }
diff --git a/tests/codegen/i128-x86-align.rs b/tests/codegen/i128-x86-align.rs
index 6b1da445c40c..ac101b72513d 100644
--- a/tests/codegen/i128-x86-align.rs
+++ b/tests/codegen/i128-x86-align.rs
@@ -19,8 +19,10 @@ pub struct ScalarPair {
 #[no_mangle]
 pub fn load(x: &ScalarPair) -> ScalarPair {
     // CHECK-LABEL: @load(
-    // CHECK-SAME: sret([32 x i8]) align 16 dereferenceable(32) %_0,
-    // CHECK-SAME: align 16 dereferenceable(32) %x
+    // CHECK-SAME: sret([32 x i8]) align 16
+    // CHECK-SAME: dereferenceable(32) %_0,
+    // CHECK-SAME: align 16
+    // CHECK-SAME: dereferenceable(32) %x
     // CHECK:      [[A:%.*]] = load i32, ptr %x, align 16
     // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr %x, i64 16
     // CHECK-NEXT: [[B:%.*]] = load i128, ptr [[GEP]], align 16
@@ -34,7 +36,8 @@ pub fn load(x: &ScalarPair) -> ScalarPair {
 #[no_mangle]
 pub fn store(x: &mut ScalarPair) {
     // CHECK-LABEL: @store(
-    // CHECK-SAME: align 16 dereferenceable(32) %x
+    // CHECK-SAME: align 16
+    // CHECK-SAME: dereferenceable(32) %x
     // CHECK:      store i32 1, ptr %x, align 16
     // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr %x, i64 16
     // CHECK-NEXT: store i128 2, ptr [[GEP]], align 16
@@ -55,8 +58,10 @@ pub fn alloca() {
 #[no_mangle]
 pub fn load_volatile(x: &ScalarPair) -> ScalarPair {
     // CHECK-LABEL: @load_volatile(
-    // CHECK-SAME: sret([32 x i8]) align 16 dereferenceable(32) %_0,
-    // CHECK-SAME: align 16 dereferenceable(32) %x
+    // CHECK-SAME: sret([32 x i8]) align 16
+    // CHECK-SAME: dereferenceable(32) %_0,
+    // CHECK-SAME: align 16
+    // CHECK-SAME: dereferenceable(32) %x
     // CHECK:      [[LOAD:%.*]] = load volatile %ScalarPair, ptr %x, align 16
     // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr %_0, align 16
     // CHECK-NEXT: ret void
@@ -66,7 +71,8 @@ pub fn load_volatile(x: &ScalarPair) -> ScalarPair {
 #[no_mangle]
 pub fn transmute(x: ScalarPair) -> (std::mem::MaybeUninit, i128) {
     // CHECK-LABEL: @transmute(
-    // CHECK-SAME:  sret([32 x i8]) align 16 dereferenceable(32) %_0,
+    // CHECK-SAME:  sret([32 x i8]) align 16
+    // CHECK-SAME:  dereferenceable(32) %_0,
     // CHECK-SAME:  i32 noundef %x.0, i128 noundef %x.1
     // CHECK:       store i32 %x.0, ptr %_0, align 16
     // CHECK-NEXT:  [[GEP:%.*]] = getelementptr inbounds i8, ptr %_0, i64 16
@@ -86,7 +92,8 @@ pub struct Struct {
 #[no_mangle]
 pub fn store_struct(x: &mut Struct) {
     // CHECK-LABEL: @store_struct(
-    // CHECK-SAME: align 16 dereferenceable(32) %x
+    // CHECK-SAME: align 16
+    // CHECK-SAME: dereferenceable(32) %x
     // CHECK:      [[TMP:%.*]] = alloca [32 x i8], align 16
     // CHECK:      store i32 1, ptr [[TMP]], align 16
     // CHECK-NEXT: [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[TMP]], i64 4
diff --git a/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs b/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs
index cba1a980caa7..b147d01b38e3 100644
--- a/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs
+++ b/tests/codegen/loongarch-abi/loongarch64-lp64d-abi.rs
@@ -259,11 +259,11 @@ pub struct IntDoubleInt {
     c: i32,
 }
 
-// CHECK: define void @f_int_double_int_s_arg(ptr noalias nocapture noundef align 8 dereferenceable(24) %a)
+// CHECK: define void @f_int_double_int_s_arg(ptr noalias{{( nocapture)?}} noundef align 8{{( captures\(none\))?}} dereferenceable(24) %a)
 #[no_mangle]
 pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}
 
-// CHECK: define void @f_ret_int_double_int_s(ptr{{( dead_on_unwind)?}} noalias nocapture noundef{{( writable)?}} sret([24 x i8]) align 8 dereferenceable(24) %_0)
+// CHECK: define void @f_ret_int_double_int_s(ptr{{( dead_on_unwind)?}} noalias{{( nocapture)?}} noundef{{( writable)?}} sret([24 x i8]) align 8{{( captures\(none\))?}} dereferenceable(24) %_0)
 #[no_mangle]
 pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt {
     IntDoubleInt { a: 1, b: 2., c: 3 }
diff --git a/tests/codegen/packed.rs b/tests/codegen/packed.rs
index 790d618b2ead..66df978d48ca 100644
--- a/tests/codegen/packed.rs
+++ b/tests/codegen/packed.rs
@@ -52,7 +52,7 @@ pub struct BigPacked2 {
 #[no_mangle]
 pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
     // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8]
-    // CHECK: call void %{{.*}}(ptr noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]])
+    // CHECK: call void %{{.*}}(ptr{{( captures(none))?}} noalias{{( nocapture)?}} noundef sret{{.*}} dereferenceable(32) [[ALLOCA]])
     // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 1 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false)
     // check that calls whose destination is a field of a packed struct
     // go through an alloca rather than calling the function with an
@@ -64,7 +64,7 @@ pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
 #[no_mangle]
 pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 {
     // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca [32 x i8]
-    // CHECK: call void %{{.*}}(ptr noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]])
+    // CHECK: call void %{{.*}}(ptr{{( captures(none))?}} noalias{{( nocapture)?}} noundef sret{{.*}} dereferenceable(32) [[ALLOCA]])
     // CHECK: call void @llvm.memcpy.{{.*}}(ptr align 2 %{{.*}}, ptr align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false)
     // check that calls whose destination is a field of a packed struct
     // go through an alloca rather than calling the function with an
diff --git a/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs
index bcd9b0eae71d..c14d5c01450b 100644
--- a/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs
+++ b/tests/codegen/riscv-abi/riscv64-lp64d-abi.rs
@@ -263,7 +263,7 @@ pub struct IntDoubleInt {
 #[no_mangle]
 pub extern "C" fn f_int_double_int_s_arg(a: IntDoubleInt) {}
 
-// CHECK: define void @f_ret_int_double_int_s(ptr {{.*}} sret([24 x i8]) align 8 dereferenceable(24) %_0)
+// CHECK: define void @f_ret_int_double_int_s(ptr {{.*}} sret([24 x i8]) align 8 {{.*}}dereferenceable(24) %_0)
 #[no_mangle]
 pub extern "C" fn f_ret_int_double_int_s() -> IntDoubleInt {
     IntDoubleInt { a: 1, b: 2., c: 3 }

From fb1ad2fe02dc7d13b6a088b535bd8c293a8085be Mon Sep 17 00:00:00 2001
From: Josh Triplett 
Date: Thu, 30 Jan 2025 11:42:19 +0100
Subject: [PATCH 271/342] Improve documentation for file locking

Add notes to each method stating that locks get dropped on close.

Clarify the return values of the try methods: they're only defined if
the lock is held via a *different* file handle/descriptor. That goes
along with the documentation that calling them while holding a lock via
the *same* file handle/descriptor may deadlock.

Document the behavior of unlock if no lock is held.
---
 library/std/src/fs.rs | 57 ++++++++++++++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 17 deletions(-)

diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 1f8aac48a9a8..a5b0111adfb4 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -629,9 +629,9 @@ impl File {
     /// This acquires an exclusive advisory lock; no other file handle to this file may acquire
     /// another lock.
     ///
-    /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is
-    /// unspecified and platform dependent, including the possibility that it will deadlock.
-    /// However, if this method returns, then an exclusive lock is held.
+    /// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact
+    /// behavior is unspecified and platform dependent, including the possibility that it will
+    /// deadlock. However, if this method returns, then an exclusive lock is held.
     ///
     /// If the file not open for writing, it is unspecified whether this function returns an error.
     ///
@@ -639,6 +639,9 @@ impl File {
     /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
     /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
     ///
+    /// The lock will be released when this file (along with any other file descriptors/handles
+    /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
+    ///
     /// # Platform-specific behavior
     ///
     /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag,
@@ -671,19 +674,22 @@ impl File {
         self.inner.lock()
     }
 
-    /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired.
+    /// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired.
     ///
     /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but
-    /// none may hold an exclusive lock.
+    /// none may hold an exclusive lock at the same time.
     ///
-    /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
-    /// unspecified and platform dependent, including the possibility that it will deadlock.
-    /// However, if this method returns, then a shared lock is held.
+    /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact
+    /// behavior is unspecified and platform dependent, including the possibility that it will
+    /// deadlock. However, if this method returns, then a shared lock is held.
     ///
     /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`],
     /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
     /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
     ///
+    /// The lock will be released when this file (along with any other file descriptors/handles
+    /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
+    ///
     /// # Platform-specific behavior
     ///
     /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag,
@@ -716,14 +722,18 @@ impl File {
         self.inner.lock_shared()
     }
 
-    /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked.
+    /// Try to acquire an exclusive advisory lock on the file.
+    ///
+    /// Returns `Ok(false)` if a different lock is already held on this file (via another
+    /// handle/descriptor).
     ///
     /// This acquires an exclusive advisory lock; no other file handle to this file may acquire
     /// another lock.
     ///
-    /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
-    /// unspecified and platform dependent, including the possibility that it will deadlock.
-    /// However, if this method returns, then an exclusive lock is held.
+    /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact
+    /// behavior is unspecified and platform dependent, including the possibility that it will
+    /// deadlock. However, if this method returns `Ok(true)`, then it has acquired an exclusive
+    /// lock.
     ///
     /// If the file not open for writing, it is unspecified whether this function returns an error.
     ///
@@ -731,6 +741,9 @@ impl File {
     /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
     /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
     ///
+    /// The lock will be released when this file (along with any other file descriptors/handles
+    /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
+    ///
     /// # Platform-specific behavior
     ///
     /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and
@@ -764,20 +777,25 @@ impl File {
         self.inner.try_lock()
     }
 
-    /// Acquire a shared advisory lock on the file.
-    /// Returns `Ok(false)` if the file is exclusively locked.
+    /// Try to acquire a shared (non-exclusive) advisory lock on the file.
+    ///
+    /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another
+    /// handle/descriptor).
     ///
     /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but
-    /// none may hold an exclusive lock.
+    /// none may hold an exclusive lock at the same time.
     ///
     /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
     /// unspecified and platform dependent, including the possibility that it will deadlock.
-    /// However, if this method returns, then a shared lock is held.
+    /// However, if this method returns `Ok(true)`, then it has acquired a shared lock.
     ///
     /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`],
     /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`]
     /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
     ///
+    /// The lock will be released when this file (along with any other file descriptors/handles
+    /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
+    ///
     /// # Platform-specific behavior
     ///
     /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and
@@ -813,7 +831,12 @@ impl File {
 
     /// Release all locks on the file.
     ///
-    /// All remaining locks are released when the file handle, and all clones of it, are dropped.
+    /// All locks are released when the file (along with any other file descriptors/handles
+    /// duplicated or inherited from it) is closed. This method allows releasing locks without
+    /// closing the file.
+    ///
+    /// If no lock is currently held via this file descriptor/handle, this method may return an
+    /// error, or may return successfully without taking any action.
     ///
     /// # Platform-specific behavior
     ///

From 9788aa0cefab1914b3df257c529a0089e01f2e5c Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Mon, 20 Jan 2025 16:39:13 +0100
Subject: [PATCH 272/342] remove redundant test

---
 tests/ui/traits/sized-coniductive.rs | 14 --------------
 1 file changed, 14 deletions(-)
 delete mode 100644 tests/ui/traits/sized-coniductive.rs

diff --git a/tests/ui/traits/sized-coniductive.rs b/tests/ui/traits/sized-coniductive.rs
deleted file mode 100644
index 5f63b166f988..000000000000
--- a/tests/ui/traits/sized-coniductive.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//@ check-pass
-// https://github.com/rust-lang/rust/issues/129541
-
-#[derive(Clone)]
-struct Test {
-    field: std::borrow::Cow<'static, [Self]>,
-}
-
-#[derive(Clone)]
-struct Hello {
-    a: <[Hello] as std::borrow::ToOwned>::Owned,
-}
-
-fn main(){}

From 12f86ee7f32efe58eab175a4fe89b30b2068fa3e Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Mon, 20 Jan 2025 16:42:03 +0100
Subject: [PATCH 273/342] move non_lifetime_binder tests

---
 .../type-alias-impl-trait/non-lifetime-binder-in-constraint.rs    | 0
 .../non-lifetime-binder-in-constraint.stderr                      | 0
 .../type-alias-impl-trait/non-lifetime-binder.rs                  | 0
 .../type-alias-impl-trait/non-lifetime-binder.stderr              | 0
 4 files changed, 0 insertions(+), 0 deletions(-)
 rename tests/ui/{ => traits/non_lifetime_binders}/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs (100%)
 rename tests/ui/{ => traits/non_lifetime_binders}/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr (100%)
 rename tests/ui/{ => traits/non_lifetime_binders}/type-alias-impl-trait/non-lifetime-binder.rs (100%)
 rename tests/ui/{ => traits/non_lifetime_binders}/type-alias-impl-trait/non-lifetime-binder.stderr (100%)

diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs
similarity index 100%
rename from tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs
rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs
diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr
similarity index 100%
rename from tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr
rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr
diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs
similarity index 100%
rename from tests/ui/type-alias-impl-trait/non-lifetime-binder.rs
rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs
diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr
similarity index 100%
rename from tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr
rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr

From fef480f4b890ae50dc818036fc0900238e5339b2 Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Mon, 20 Jan 2025 16:42:30 +0100
Subject: [PATCH 274/342] error pattern to correct line

---
 tests/ui/generic-associated-types/issue-90014-tait2.rs     | 3 +--
 tests/ui/generic-associated-types/issue-90014-tait2.stderr | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.rs b/tests/ui/generic-associated-types/issue-90014-tait2.rs
index ef54a89aaae5..3f7a9ff63c31 100644
--- a/tests/ui/generic-associated-types/issue-90014-tait2.rs
+++ b/tests/ui/generic-associated-types/issue-90014-tait2.rs
@@ -3,8 +3,6 @@
 //! Unfortunately we don't even reach opaque type collection, as we ICE in typeck before that.
 //! See #109281 for the original report.
 //@ edition:2018
-//@ error-pattern: expected generic lifetime parameter, found `'a`
-
 #![feature(type_alias_impl_trait)]
 
 use std::future::Future;
@@ -24,6 +22,7 @@ impl<'x, T: 'x> Trait<'x> for (T,) {
 impl Foo<'_> {
     fn make_fut(&self) -> Box Trait<'a, Thing = Fut<'a>>> {
         Box::new((async { () },))
+        //~^ ERROR expected generic lifetime parameter, found `'a`
     }
 }
 
diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.stderr b/tests/ui/generic-associated-types/issue-90014-tait2.stderr
index be6f4272ce18..aa427d42649a 100644
--- a/tests/ui/generic-associated-types/issue-90014-tait2.stderr
+++ b/tests/ui/generic-associated-types/issue-90014-tait2.stderr
@@ -1,5 +1,5 @@
 error[E0792]: expected generic lifetime parameter, found `'a`
-  --> $DIR/issue-90014-tait2.rs:26:9
+  --> $DIR/issue-90014-tait2.rs:24:9
    |
 LL | type Fut<'a> = impl Future;
    |          -- this generic parameter must be used with a generic lifetime parameter

From 8c078fde36b8b7d6757ff2bea271ecb2a85cf2eb Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Mon, 20 Jan 2025 16:42:50 +0100
Subject: [PATCH 275/342] merge tests

---
 .../129541-recursive-struct-and-array-impl.rs | 23 -------------------
 .../solver-cycles/129541-recursive-struct.rs  |  5 ++++
 2 files changed, 5 insertions(+), 23 deletions(-)
 delete mode 100644 tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs

diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs
deleted file mode 100644
index defb39aae06d..000000000000
--- a/tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Regression test for #129541
-
-//@ check-pass
-
-trait Bound {}
-trait Normalize {
-    type Assoc;
-}
-
-impl Normalize for T {
-    type Assoc = T;
-}
-
-impl Normalize for [T] {
-    type Assoc = T;
-}
-
-impl Bound for Hello {}
-struct Hello {
-    a: <[Hello] as Normalize>::Assoc,
-}
-
-fn main() {}
diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs
index d4339dd54d6c..4fbcbefec913 100644
--- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs
+++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs
@@ -1,5 +1,6 @@
 // Regression test for #129541
 
+//@ revisions: unique multiple
 //@ check-pass
 
 trait Bound {}
@@ -7,6 +8,10 @@ trait Normalize {
     type Assoc;
 }
 
+#[cfg(multiple)]
+impl Normalize for T {
+    type Assoc = T;
+}
 impl Normalize for [T] {
     type Assoc = T;
 }

From fa6b95fc4e2e0f36d52cea164544651049038074 Mon Sep 17 00:00:00 2001
From: lcnr 
Date: Mon, 20 Jan 2025 16:42:58 +0100
Subject: [PATCH 276/342] update comment

---
 tests/ui/wf/wf-trait-default-fn-ret.rs     | 3 +--
 tests/ui/wf/wf-trait-default-fn-ret.stderr | 4 ++--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/tests/ui/wf/wf-trait-default-fn-ret.rs b/tests/ui/wf/wf-trait-default-fn-ret.rs
index 2103dae8d233..a7f83dcf678a 100644
--- a/tests/ui/wf/wf-trait-default-fn-ret.rs
+++ b/tests/ui/wf/wf-trait-default-fn-ret.rs
@@ -1,5 +1,4 @@
-// Check that we test WF conditions for fn arguments. Because the
-// current code is so goofy, this is only a warning for now.
+// Check that we test WF conditions for fn arguments.
 
 #![feature(rustc_attrs)]
 #![allow(dead_code)]
diff --git a/tests/ui/wf/wf-trait-default-fn-ret.stderr b/tests/ui/wf/wf-trait-default-fn-ret.stderr
index f749ac7b1b3e..f0d1b55edc49 100644
--- a/tests/ui/wf/wf-trait-default-fn-ret.stderr
+++ b/tests/ui/wf/wf-trait-default-fn-ret.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `Self: Eq` is not satisfied
-  --> $DIR/wf-trait-default-fn-ret.rs:11:22
+  --> $DIR/wf-trait-default-fn-ret.rs:10:22
    |
 LL |     fn bar(&self) -> Bar {
    |                      ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
    |
 note: required by a bound in `Bar`
-  --> $DIR/wf-trait-default-fn-ret.rs:8:14
+  --> $DIR/wf-trait-default-fn-ret.rs:7:14
    |
 LL | struct Bar { value: Box }
    |              ^^ required by this bound in `Bar`

From 6e457b88eb6058267794ab6dbf595cf8f183368c Mon Sep 17 00:00:00 2001
From: Hans Wennborg 
Date: Thu, 30 Jan 2025 12:30:14 +0100
Subject: [PATCH 277/342] use Attribute::getWithCaptureInfo

---
 compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 858251ff9c8c..545422682f0b 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -438,8 +438,7 @@ LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) {
 #if LLVM_VERSION_GE(21, 0)
   // LLVM 21 replaced the NoCapture attribute with Captures(none).
   if (RustAttr == LLVMRustAttributeKind::NoCapture) {
-    return wrap(Attribute::get(*unwrap(C), Attribute::Captures,
-                               CaptureInfo::none().toIntValue()));
+    return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none()));
   }
 #endif
   return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr)));

From 6b699ccee499949b5e4e222976e1ac8887412a5c Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Thu, 30 Jan 2025 13:44:13 +0100
Subject: [PATCH 278/342] float::min/max: mention the non-determinism around
 signed 0

---
 library/core/src/num/f128.rs | 6 ++++--
 library/core/src/num/f16.rs  | 6 ++++--
 library/core/src/num/f32.rs  | 6 ++++--
 library/core/src/num/f64.rs  | 6 ++++--
 4 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index 8fb1588e60b3..5e45974b3d42 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -670,7 +670,8 @@ impl f128 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids maxNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmax.
+    /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// #![feature(f128)]
@@ -696,7 +697,8 @@ impl f128 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids minNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmin.
+    /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// #![feature(f128)]
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index 8c2af74b8f84..e3176cd16885 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -662,7 +662,8 @@ impl f16 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids maxNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmax.
+    /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// #![feature(f16)]
@@ -687,7 +688,8 @@ impl f16 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids minNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmin.
+    /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// #![feature(f16)]
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 817bedbd44f9..4d42997369ff 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -874,7 +874,8 @@ impl f32 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids maxNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmax.
+    /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// let x = 1.0f32;
@@ -895,7 +896,8 @@ impl f32 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids minNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmin.
+    /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// let x = 1.0f32;
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 1b0651a0def0..907971d303ff 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -892,7 +892,8 @@ impl f64 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids maxNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmax.
+    /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// let x = 1.0_f64;
@@ -913,7 +914,8 @@ impl f64 {
     /// If one of the arguments is NaN, then the other argument is returned.
     /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs;
     /// this function handles all NaNs the same way and avoids minNum's problems with associativity.
-    /// This also matches the behavior of libm’s fmin.
+    /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal
+    /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically.
     ///
     /// ```
     /// let x = 1.0_f64;

From 447539324a15b118d6d77a200445b41beb98742e Mon Sep 17 00:00:00 2001
From: Zeeshan Ali Khan 
Date: Thu, 30 Jan 2025 13:53:03 +0100
Subject: [PATCH 279/342] Fix a typo in profile-guided-optimization.md

It's either "profile-guided" or "profiled-guided" and I think it'sw the former. :)
---
 src/doc/rustc/src/profile-guided-optimization.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md
index 38b655b75422..4050494793a3 100644
--- a/src/doc/rustc/src/profile-guided-optimization.md
+++ b/src/doc/rustc/src/profile-guided-optimization.md
@@ -3,7 +3,7 @@
 `rustc` supports doing profile-guided optimization (PGO).
 This chapter describes what PGO is, what it is good for, and how it can be used.
 
-## What Is Profiled-Guided Optimization?
+## What Is Profile-Guided Optimization?
 
 The basic concept of PGO is to collect data about the typical execution of
 a program (e.g. which branches it is likely to take) and then use this data

From 46b7da82434660068b0fb8b5c19a42c4299ad736 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Thu, 30 Jan 2025 14:40:08 +0100
Subject: [PATCH 280/342] atomic: extend compare_and_swap migration docs

---
 library/core/src/sync/atomic.rs | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 0061e33f9864..73180bde54aa 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -716,6 +716,12 @@ impl AtomicBool {
     /// AcqRel   | AcqRel  | Acquire
     /// SeqCst   | SeqCst  | SeqCst
     ///
+    /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use
+    /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`,
+    /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err`
+    /// rather than to infer success vs failure based on the value that was read.
+    ///
+    /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead.
     /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds,
     /// which allows the compiler to generate better assembly code when the compare and swap
     /// is used in a loop.
@@ -1651,6 +1657,12 @@ impl AtomicPtr {
     /// AcqRel   | AcqRel  | Acquire
     /// SeqCst   | SeqCst  | SeqCst
     ///
+    /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use
+    /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`,
+    /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err`
+    /// rather than to infer success vs failure based on the value that was read.
+    ///
+    /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead.
     /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds,
     /// which allows the compiler to generate better assembly code when the compare and swap
     /// is used in a loop.
@@ -2771,6 +2783,12 @@ macro_rules! atomic_int {
             /// AcqRel   | AcqRel  | Acquire
             /// SeqCst   | SeqCst  | SeqCst
             ///
+            /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use
+            /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`,
+            /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err`
+            /// rather than to infer success vs failure based on the value that was read.
+            ///
+            /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead.
             /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds,
             /// which allows the compiler to generate better assembly code when the compare and swap
             /// is used in a loop.

From 08d7e9dfe510196c3437662aaf1c5e6915941f94 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 10 Jan 2025 20:09:10 +0000
Subject: [PATCH 281/342] Rework rustc_dump_vtable

---
 compiler/rustc_feature/src/builtin_attrs.rs   |   2 +-
 .../rustc_hir_analysis/src/collect/dump.rs    |  82 +++-
 compiler/rustc_hir_analysis/src/lib.rs        |  13 +-
 compiler/rustc_trait_selection/messages.ftl   |   2 -
 compiler/rustc_trait_selection/src/errors.rs  |  11 +-
 .../src/traits/vtable.rs                      |  17 +-
 .../rustc-dev-guide/src/compiler-debugging.md |   2 +-
 ...ltiple-supertraits-modulo-binder-vtable.rs |  25 ++
 ...le-supertraits-modulo-binder-vtable.stderr |  28 ++
 ...supertraits-modulo-normalization-vtable.rs |  36 ++
 ...rtraits-modulo-normalization-vtable.stderr |  28 ++
 tests/ui/traits/vtable/multiple-markers.rs    |  33 +-
 .../ui/traits/vtable/multiple-markers.stderr  |  32 +-
 tests/ui/traits/vtable/vtable-diamond.rs      |  31 +-
 tests/ui/traits/vtable/vtable-diamond.stderr  |  57 ++-
 .../traits/vtable/vtable-dyn-incompatible.rs  |   7 +-
 .../vtable/vtable-dyn-incompatible.stderr     |  16 +-
 tests/ui/traits/vtable/vtable-multi-level.rs  |  99 +++--
 .../traits/vtable/vtable-multi-level.stderr   | 361 +++++++++---------
 tests/ui/traits/vtable/vtable-multiple.rs     |  22 +-
 tests/ui/traits/vtable/vtable-multiple.stderr |  43 ++-
 tests/ui/traits/vtable/vtable-vacant.rs       |  15 +-
 tests/ui/traits/vtable/vtable-vacant.stderr   |  22 +-
 23 files changed, 603 insertions(+), 381 deletions(-)
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr

diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 17433eed9e76..684fc5e37e0b 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -1136,7 +1136,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         TEST, rustc_dump_vtable, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes
+        WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
         TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/),
diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs
index f1022d957531..d990d824200a 100644
--- a/compiler/rustc_hir_analysis/src/collect/dump.rs
+++ b/compiler/rustc_hir_analysis/src/collect/dump.rs
@@ -1,7 +1,8 @@
+use rustc_hir as hir;
 use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
 use rustc_hir::intravisit;
 use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
 use rustc_span::sym;
 
 pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) {
@@ -87,3 +88,82 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
         }
     }
 }
+
+pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
+    for id in tcx.hir().items() {
+        let def_id = id.owner_id.def_id;
+
+        let Some(attr) = tcx.get_attr(def_id, sym::rustc_dump_vtable) else {
+            continue;
+        };
+
+        let vtable_entries = match tcx.hir().item(id).kind {
+            hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => {
+                let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity();
+                if trait_ref.has_non_region_param() {
+                    tcx.dcx().span_err(
+                        attr.span,
+                        "`rustc_dump_vtable` must be applied to non-generic impl",
+                    );
+                    continue;
+                }
+                if !tcx.is_dyn_compatible(trait_ref.def_id) {
+                    tcx.dcx().span_err(
+                        attr.span,
+                        "`rustc_dump_vtable` must be applied to dyn-compatible trait",
+                    );
+                    continue;
+                }
+                let Ok(trait_ref) = tcx
+                    .try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref)
+                else {
+                    tcx.dcx().span_err(
+                        attr.span,
+                        "`rustc_dump_vtable` applied to impl header that cannot be normalized",
+                    );
+                    continue;
+                };
+                tcx.vtable_entries(ty::Binder::dummy(trait_ref))
+            }
+            hir::ItemKind::TyAlias(_, _) => {
+                let ty = tcx.type_of(def_id).instantiate_identity();
+                if ty.has_non_region_param() {
+                    tcx.dcx().span_err(
+                        attr.span,
+                        "`rustc_dump_vtable` must be applied to non-generic type",
+                    );
+                    continue;
+                }
+                let Ok(ty) =
+                    tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty)
+                else {
+                    tcx.dcx().span_err(
+                        attr.span,
+                        "`rustc_dump_vtable` applied to type alias that cannot be normalized",
+                    );
+                    continue;
+                };
+                let ty::Dynamic(data, _, _) = *ty.kind() else {
+                    tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type");
+                    continue;
+                };
+                if let Some(principal) = data.principal() {
+                    tcx.vtable_entries(
+                        principal.map_bound(|principal| principal.with_self_ty(tcx, ty)),
+                    )
+                } else {
+                    TyCtxt::COMMON_VTABLE_ENTRIES
+                }
+            }
+            _ => {
+                tcx.dcx().span_err(
+                    attr.span,
+                    "`rustc_dump_vtable` only applies to impl, or type alias of dyn type",
+                );
+                continue;
+            }
+        };
+
+        tcx.dcx().span_err(tcx.def_span(def_id), format!("vtable entries: {vtable_entries:#?}"));
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index bc7d4365eeef..03dd0cccf757 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -152,11 +152,14 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
     });
 
     if tcx.features().rustc_attrs() {
-        tcx.sess.time("outlives_dumping", || outlives::dump::inferred_outlives(tcx));
-        tcx.sess.time("variance_dumping", || variance::dump::variances(tcx));
-        collect::dump::opaque_hidden_types(tcx);
-        collect::dump::predicates_and_item_bounds(tcx);
-        collect::dump::def_parents(tcx);
+        tcx.sess.time("dumping_rustc_attr_data", || {
+            outlives::dump::inferred_outlives(tcx);
+            variance::dump::variances(tcx);
+            collect::dump::opaque_hidden_types(tcx);
+            collect::dump::predicates_and_item_bounds(tcx);
+            collect::dump::def_parents(tcx);
+            collect::dump::vtables(tcx);
+        });
     }
 
     // Make sure we evaluate all static and (non-associated) const items, even if unused.
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 7c72318b4d78..055a3edcc329 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -148,8 +148,6 @@ trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement
 trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement
 trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement
 
-trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
-
 trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
     .label = empty on-clause here
 
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index c8672b9dbd2f..c5ab8a71c780 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
 use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
-use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
+use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt};
 use rustc_span::{BytePos, Ident, Span, Symbol, kw};
 
 use crate::error_reporting::infer::ObligationCauseAsDiagArg;
@@ -22,15 +22,6 @@ use crate::fluent_generated as fluent;
 
 pub mod note_and_explain;
 
-#[derive(Diagnostic)]
-#[diag(trait_selection_dump_vtable_entries)]
-pub struct DumpVTableEntries<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub trait_ref: PolyTraitRef<'a>,
-    pub entries: String,
-}
-
 #[derive(Diagnostic)]
 #[diag(trait_selection_unable_to_construct_constant_value)]
 pub struct UnableToConstructConstantValue<'a> {
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index b5bc8364c7b6..72ddf01c7913 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -11,11 +11,10 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::{
     self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry,
 };
-use rustc_span::{DUMMY_SP, Span, sym};
+use rustc_span::DUMMY_SP;
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
 
-use crate::errors::DumpVTableEntries;
 use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
 
 #[derive(Clone, Debug)]
@@ -192,15 +191,6 @@ fn maybe_iter(i: Option) -> impl Iterator {
     i.into_iter().flatten()
 }
 
-fn dump_vtable_entries<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    sp: Span,
-    trait_ref: ty::PolyTraitRef<'tcx>,
-    entries: &[VtblEntry<'tcx>],
-) {
-    tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") });
-}
-
 fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
     own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
 }
@@ -317,11 +307,6 @@ fn vtable_entries<'tcx>(
 
     let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
 
-    if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
-        let sp = tcx.def_span(trait_ref.def_id());
-        dump_vtable_entries(tcx, sp, trait_ref, &entries);
-    }
-
     tcx.arena.alloc_from_iter(entries)
 }
 
diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md
index a2a6c8085df5..e2097b26e5c8 100644
--- a/src/doc/rustc-dev-guide/src/compiler-debugging.md
+++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md
@@ -275,7 +275,7 @@ Here are some notable ones:
 | `rustc_dump_def_parents` | Dumps the chain of `DefId` parents of certain definitions. |
 | `rustc_dump_item_bounds` | Dumps the [`item_bounds`] of an item. |
 | `rustc_dump_predicates` | Dumps the [`predicates_of`] an item. |
-| `rustc_dump_vtable` |  |
+| `rustc_dump_vtable` | Dumps the vtable layout of an impl, or a type alias of a dyn type. |
 | `rustc_hidden_type_of_opaques` | Dumps the [hidden type of each opaque types][opaq] in the crate. |
 | `rustc_layout` | [See this section](#debugging-type-layouts). |
 | `rustc_object_lifetime_default` | Dumps the [object lifetime defaults] of an item. |
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs
new file mode 100644
index 000000000000..b41cf2e9f267
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs
@@ -0,0 +1,25 @@
+#![feature(rustc_attrs)]
+
+trait Supertrait {
+    fn _print_numbers(&self, mem: &[usize; 100]) {
+    }
+}
+impl Supertrait for () {}
+
+trait Trait: Supertrait + Supertrait {
+    fn say_hello(&self, _: &usize) {
+    }
+}
+impl Trait for () {}
+
+// We should observe compatibility between these two vtables.
+
+#[rustc_dump_vtable]
+type First = dyn for<'a> Trait<&'static (), &'a ()>;
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
+type Second = dyn Trait<&'static (), &'static ()>;
+//~^ ERROR vtable entries
+
+fn main() {}
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
new file mode 100644
index 000000000000..1b99be4ab880
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
@@ -0,0 +1,28 @@
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
+           Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
+           TraitVPtr(for<'a>  Trait<&(), &'a ()> as Supertrait<&'a ()>>),
+           Method( Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)),
+       ]
+  --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1
+   |
+LL | type First = dyn for<'a> Trait<&'static (), &'a ()>;
+   | ^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method( as Supertrait<&()>>::_print_numbers - shim(reify)),
+           Method( as Trait<&(), &()>>::say_hello - shim(reify)),
+       ]
+  --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:22:1
+   |
+LL | type Second = dyn Trait<&'static (), &'static ()>;
+   | ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs
new file mode 100644
index 000000000000..22d6bf94d870
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs
@@ -0,0 +1,36 @@
+#![feature(rustc_attrs)]
+
+#![feature(trait_upcasting)]
+
+trait Supertrait {
+    fn _print_numbers(&self, mem: &[usize; 100]) {
+        println!("{mem:?}");
+    }
+}
+impl Supertrait for () {}
+
+trait Identity {
+    type Selff;
+}
+impl Identity for Selff {
+    type Selff = Selff;
+}
+
+trait Middle: Supertrait<()> + Supertrait {
+    fn say_hello(&self, _: &usize) {
+        println!("Hello!");
+    }
+}
+impl Middle for () {}
+
+trait Trait: Middle<<() as Identity>::Selff> {}
+
+#[rustc_dump_vtable]
+impl Trait for () {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
+type Virtual = dyn Middle<()>;
+//~^ ERROR vtable entries
+
+fn main() {}
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
new file mode 100644
index 000000000000..9e93e1c48c9e
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
@@ -0,0 +1,28 @@
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(<() as Supertrait<()>>::_print_numbers),
+           Method(<() as Supertrait<()>>::_print_numbers),
+           TraitVPtr(<() as Supertrait<<() as Identity>::Selff>>),
+           Method(<() as Middle<()>>::say_hello),
+       ]
+  --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1
+   |
+LL | impl Trait for () {}
+   | ^^^^^^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method( as Supertrait<()>>::_print_numbers - shim(reify)),
+           Method( as Middle<()>>::say_hello - shim(reify)),
+       ]
+  --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:33:1
+   |
+LL | type Virtual = dyn Middle<()>;
+   | ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/traits/vtable/multiple-markers.rs b/tests/ui/traits/vtable/multiple-markers.rs
index 8a9e7a006cf5..8eba5135582d 100644
--- a/tests/ui/traits/vtable/multiple-markers.rs
+++ b/tests/ui/traits/vtable/multiple-markers.rs
@@ -2,8 +2,7 @@
 //
 // This test makes sure that multiple marker (method-less) traits can reuse the
 // same pointer for upcasting.
-//
-//@ build-fail
+
 #![crate_type = "lib"]
 #![feature(rustc_attrs)]
 
@@ -17,17 +16,13 @@ trait T {
     fn method(&self) {}
 }
 
-#[rustc_dump_vtable]
-trait A: M0 + M1 + M2 + T {} //~ error: vtable entries for ``:
+trait A: M0 + M1 + M2 + T {}
 
-#[rustc_dump_vtable]
-trait B: M0 + M1 + T + M2 {} //~ error: vtable entries for ``:
+trait B: M0 + M1 + T + M2 {}
 
-#[rustc_dump_vtable]
-trait C: M0 + T + M1 + M2 {} //~ error: vtable entries for ``:
+trait C: M0 + T + M1 + M2 {}
 
-#[rustc_dump_vtable]
-trait D: T + M0 + M1 + M2 {} //~ error: vtable entries for ``:
+trait D: T + M0 + M1 + M2 {}
 
 struct S;
 
@@ -35,13 +30,21 @@ impl M0 for S {}
 impl M1 for S {}
 impl M2 for S {}
 impl T for S {}
+
+#[rustc_dump_vtable]
 impl A for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl B for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl C for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl D for S {}
+//~^ ERROR vtable entries
 
-pub fn require_vtables() {
-    fn require_vtables(_: &dyn A, _: &dyn B, _: &dyn C, _: &dyn D) {}
-
-    require_vtables(&S, &S, &S, &S)
-}
+fn main() {}
diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr
index 36ac8b24eb53..35dd3c2516d8 100644
--- a/tests/ui/traits/vtable/multiple-markers.stderr
+++ b/tests/ui/traits/vtable/multiple-markers.stderr
@@ -1,46 +1,46 @@
-error: vtable entries for ``: [
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
            Method(::method),
        ]
-  --> $DIR/multiple-markers.rs:21:1
+  --> $DIR/multiple-markers.rs:35:1
    |
-LL | trait A: M0 + M1 + M2 + T {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl A for S {}
+   | ^^^^^^^^^^^^
 
-error: vtable entries for ``: [
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
            Method(::method),
        ]
-  --> $DIR/multiple-markers.rs:24:1
+  --> $DIR/multiple-markers.rs:39:1
    |
-LL | trait B: M0 + M1 + T + M2 {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl B for S {}
+   | ^^^^^^^^^^^^
 
-error: vtable entries for ``: [
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
            Method(::method),
        ]
-  --> $DIR/multiple-markers.rs:27:1
+  --> $DIR/multiple-markers.rs:43:1
    |
-LL | trait C: M0 + T + M1 + M2 {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl C for S {}
+   | ^^^^^^^^^^^^
 
-error: vtable entries for ``: [
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
            Method(::method),
        ]
-  --> $DIR/multiple-markers.rs:30:1
+  --> $DIR/multiple-markers.rs:47:1
    |
-LL | trait D: T + M0 + M1 + M2 {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl D for S {}
+   | ^^^^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/traits/vtable/vtable-diamond.rs b/tests/ui/traits/vtable/vtable-diamond.rs
index 2cfa86c526de..56053f6d0260 100644
--- a/tests/ui/traits/vtable/vtable-diamond.rs
+++ b/tests/ui/traits/vtable/vtable-diamond.rs
@@ -1,44 +1,37 @@
-//@ build-fail
 #![feature(rustc_attrs)]
 
-#[rustc_dump_vtable]
 trait A {
     fn foo_a(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait B: A {
     fn foo_b(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait C: A {
-    //~^ error vtable
     fn foo_c(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait D: B + C {
-    //~^ error vtable
     fn foo_d(&self) {}
 }
 
 struct S;
 
+#[rustc_dump_vtable]
 impl A for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl B for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl C for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl D for S {}
+//~^ ERROR vtable entries
 
-fn foo(d: &dyn D) {
-    d.foo_d();
-}
-
-fn bar(d: &dyn C) {
-    d.foo_c();
-}
-
-fn main() {
-    foo(&S);
-    bar(&S);
-}
+fn main() {}
diff --git a/tests/ui/traits/vtable/vtable-diamond.stderr b/tests/ui/traits/vtable/vtable-diamond.stderr
index f3718c5d852f..4644bf339b17 100644
--- a/tests/ui/traits/vtable/vtable-diamond.stderr
+++ b/tests/ui/traits/vtable/vtable-diamond.stderr
@@ -1,4 +1,39 @@
-error: vtable entries for ``: [
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+       ]
+  --> $DIR/vtable-diamond.rs:22:1
+   |
+LL | impl A for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+           Method(::foo_b),
+       ]
+  --> $DIR/vtable-diamond.rs:26:1
+   |
+LL | impl B for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+           Method(::foo_c),
+       ]
+  --> $DIR/vtable-diamond.rs:30:1
+   |
+LL | impl C for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
@@ -8,22 +43,10 @@ error: vtable entries for ``: [
            TraitVPtr(),
            Method(::foo_d),
        ]
-  --> $DIR/vtable-diamond.rs:21:1
+  --> $DIR/vtable-diamond.rs:34:1
    |
-LL | trait D: B + C {
-   | ^^^^^^^^^^^^^^
+LL | impl D for S {}
+   | ^^^^^^^^^^^^
 
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_a),
-           Method(::foo_c),
-       ]
-  --> $DIR/vtable-diamond.rs:15:1
-   |
-LL | trait C: A {
-   | ^^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.rs b/tests/ui/traits/vtable/vtable-dyn-incompatible.rs
index 64a8138bdcfb..fb19dec4ace1 100644
--- a/tests/ui/traits/vtable/vtable-dyn-incompatible.rs
+++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.rs
@@ -1,15 +1,16 @@
-//@ build-fail
 #![feature(rustc_attrs)]
 
 // Ensure that dyn-incompatible methods in Iterator does not generate
 // vtable entries.
 
-#[rustc_dump_vtable]
 trait A: Iterator {}
-//~^ error vtable
 
 impl A for T where T: Iterator {}
 
+#[rustc_dump_vtable]
+type Test = dyn A;
+//~^ error vtable
+
 fn foo(_a: &mut dyn A) {
 }
 
diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr
index e442c3eac00e..c80a763998b1 100644
--- a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr
+++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr
@@ -1,16 +1,16 @@
-error: vtable entries for ` as A>`: [
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
-           Method( as Iterator>::next),
-           Method( as Iterator>::size_hint),
-           Method( as Iterator>::advance_by),
-           Method( as Iterator>::nth),
+           Method( as Iterator>::next - shim(reify)),
+           Method( as Iterator>::size_hint - shim(reify)),
+           Method( as Iterator>::advance_by - shim(reify)),
+           Method( as Iterator>::nth - shim(reify)),
        ]
-  --> $DIR/vtable-dyn-incompatible.rs:8:1
+  --> $DIR/vtable-dyn-incompatible.rs:11:1
    |
-LL | trait A: Iterator {}
-   | ^^^^^^^^^^^^^^^^^
+LL | type Test = dyn A;
+   | ^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/vtable/vtable-multi-level.rs b/tests/ui/traits/vtable/vtable-multi-level.rs
index bedbf84d3039..67bac49f9f66 100644
--- a/tests/ui/traits/vtable/vtable-multi-level.rs
+++ b/tests/ui/traits/vtable/vtable-multi-level.rs
@@ -1,4 +1,3 @@
-//@ build-fail
 #![feature(rustc_attrs)]
 
 //   O --> G --> C --> A
@@ -10,134 +9,126 @@
 //           |-> M --> K
 //                 \-> L
 
-#[rustc_dump_vtable]
 trait A {
-    //~^ error vtable
     fn foo_a(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait B {
-    //~^ error vtable
     fn foo_b(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait C: A + B {
-    //~^ error vtable
     fn foo_c(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait D {
-    //~^ error vtable
     fn foo_d(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait E {
-    //~^ error vtable
     fn foo_e(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait F: D + E {
-    //~^ error vtable
     fn foo_f(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait G: C + F {
     fn foo_g(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait H {
-    //~^ error vtable
     fn foo_h(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait I {
-    //~^ error vtable
     fn foo_i(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait J: H + I {
-    //~^ error vtable
     fn foo_j(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait K {
-    //~^ error vtable
     fn foo_k(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait L {
-    //~^ error vtable
     fn foo_l(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait M: K + L {
-    //~^ error vtable
     fn foo_m(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait N: J + M {
-    //~^ error vtable
     fn foo_n(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait O: G + N {
-    //~^ error vtable
     fn foo_o(&self) {}
 }
 
 struct S;
 
+#[rustc_dump_vtable]
 impl A for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl B for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl C for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl D for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl E for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl F for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl G for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl H for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl I for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl J for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl K for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl L for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl M for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl N for S {}
+//~^ ERROR vtable entries
+
+#[rustc_dump_vtable]
 impl O for S {}
+//~^ ERROR vtable entries
 
-macro_rules! monomorphize_vtable {
-    ($trait:ident) => {{
-        fn foo(_ : &dyn $trait) {}
-        foo(&S);
-    }}
-}
-
-fn main() {
-    monomorphize_vtable!(O);
-
-    monomorphize_vtable!(A);
-    monomorphize_vtable!(B);
-    monomorphize_vtable!(C);
-    monomorphize_vtable!(D);
-    monomorphize_vtable!(E);
-    monomorphize_vtable!(F);
-    monomorphize_vtable!(H);
-    monomorphize_vtable!(I);
-    monomorphize_vtable!(J);
-    monomorphize_vtable!(K);
-    monomorphize_vtable!(L);
-    monomorphize_vtable!(M);
-    monomorphize_vtable!(N);
-}
+fn main() {}
diff --git a/tests/ui/traits/vtable/vtable-multi-level.stderr b/tests/ui/traits/vtable/vtable-multi-level.stderr
index c4389e23fc10..961900aa3d29 100644
--- a/tests/ui/traits/vtable/vtable-multi-level.stderr
+++ b/tests/ui/traits/vtable/vtable-multi-level.stderr
@@ -1,4 +1,190 @@
-error: vtable entries for ``: [
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+       ]
+  --> $DIR/vtable-multi-level.rs:75:1
+   |
+LL | impl A for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_b),
+       ]
+  --> $DIR/vtable-multi-level.rs:79:1
+   |
+LL | impl B for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+           Method(::foo_b),
+           TraitVPtr(),
+           Method(::foo_c),
+       ]
+  --> $DIR/vtable-multi-level.rs:83:1
+   |
+LL | impl C for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_d),
+       ]
+  --> $DIR/vtable-multi-level.rs:87:1
+   |
+LL | impl D for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_e),
+       ]
+  --> $DIR/vtable-multi-level.rs:91:1
+   |
+LL | impl E for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_d),
+           Method(::foo_e),
+           TraitVPtr(),
+           Method(::foo_f),
+       ]
+  --> $DIR/vtable-multi-level.rs:95:1
+   |
+LL | impl F for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+           Method(::foo_b),
+           TraitVPtr(),
+           Method(::foo_c),
+           Method(::foo_d),
+           TraitVPtr(),
+           Method(::foo_e),
+           TraitVPtr(),
+           Method(::foo_f),
+           TraitVPtr(),
+           Method(::foo_g),
+       ]
+  --> $DIR/vtable-multi-level.rs:99:1
+   |
+LL | impl G for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_h),
+       ]
+  --> $DIR/vtable-multi-level.rs:103:1
+   |
+LL | impl H for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_i),
+       ]
+  --> $DIR/vtable-multi-level.rs:107:1
+   |
+LL | impl I for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_h),
+           Method(::foo_i),
+           TraitVPtr(),
+           Method(::foo_j),
+       ]
+  --> $DIR/vtable-multi-level.rs:111:1
+   |
+LL | impl J for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_k),
+       ]
+  --> $DIR/vtable-multi-level.rs:115:1
+   |
+LL | impl K for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_l),
+       ]
+  --> $DIR/vtable-multi-level.rs:119:1
+   |
+LL | impl L for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_k),
+           Method(::foo_l),
+           TraitVPtr(),
+           Method(::foo_m),
+       ]
+  --> $DIR/vtable-multi-level.rs:123:1
+   |
+LL | impl M for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_h),
+           Method(::foo_i),
+           TraitVPtr(),
+           Method(::foo_j),
+           Method(::foo_k),
+           TraitVPtr(),
+           Method(::foo_l),
+           TraitVPtr(),
+           Method(::foo_m),
+           TraitVPtr(),
+           Method(::foo_n),
+       ]
+  --> $DIR/vtable-multi-level.rs:127:1
+   |
+LL | impl N for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
@@ -29,175 +215,10 @@ error: vtable entries for ``: [
            TraitVPtr(),
            Method(::foo_o),
        ]
-  --> $DIR/vtable-multi-level.rs:97:1
+  --> $DIR/vtable-multi-level.rs:131:1
    |
-LL | trait O: G + N {
-   | ^^^^^^^^^^^^^^
+LL | impl O for S {}
+   | ^^^^^^^^^^^^
 
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_a),
-       ]
-  --> $DIR/vtable-multi-level.rs:14:1
-   |
-LL | trait A {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_b),
-       ]
-  --> $DIR/vtable-multi-level.rs:20:1
-   |
-LL | trait B {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_a),
-           Method(::foo_b),
-           TraitVPtr(),
-           Method(::foo_c),
-       ]
-  --> $DIR/vtable-multi-level.rs:26:1
-   |
-LL | trait C: A + B {
-   | ^^^^^^^^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_d),
-       ]
-  --> $DIR/vtable-multi-level.rs:32:1
-   |
-LL | trait D {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_e),
-       ]
-  --> $DIR/vtable-multi-level.rs:38:1
-   |
-LL | trait E {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_d),
-           Method(::foo_e),
-           TraitVPtr(),
-           Method(::foo_f),
-       ]
-  --> $DIR/vtable-multi-level.rs:44:1
-   |
-LL | trait F: D + E {
-   | ^^^^^^^^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_h),
-       ]
-  --> $DIR/vtable-multi-level.rs:55:1
-   |
-LL | trait H {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_i),
-       ]
-  --> $DIR/vtable-multi-level.rs:61:1
-   |
-LL | trait I {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_h),
-           Method(::foo_i),
-           TraitVPtr(),
-           Method(::foo_j),
-       ]
-  --> $DIR/vtable-multi-level.rs:67:1
-   |
-LL | trait J: H + I {
-   | ^^^^^^^^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_k),
-       ]
-  --> $DIR/vtable-multi-level.rs:73:1
-   |
-LL | trait K {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_l),
-       ]
-  --> $DIR/vtable-multi-level.rs:79:1
-   |
-LL | trait L {
-   | ^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_k),
-           Method(::foo_l),
-           TraitVPtr(),
-           Method(::foo_m),
-       ]
-  --> $DIR/vtable-multi-level.rs:85:1
-   |
-LL | trait M: K + L {
-   | ^^^^^^^^^^^^^^
-
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_h),
-           Method(::foo_i),
-           TraitVPtr(),
-           Method(::foo_j),
-           Method(::foo_k),
-           TraitVPtr(),
-           Method(::foo_l),
-           TraitVPtr(),
-           Method(::foo_m),
-           TraitVPtr(),
-           Method(::foo_n),
-       ]
-  --> $DIR/vtable-multi-level.rs:91:1
-   |
-LL | trait N: J + M {
-   | ^^^^^^^^^^^^^^
-
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/traits/vtable/vtable-multiple.rs b/tests/ui/traits/vtable/vtable-multiple.rs
index beaaf4db6b1c..51e20ee20c9f 100644
--- a/tests/ui/traits/vtable/vtable-multiple.rs
+++ b/tests/ui/traits/vtable/vtable-multiple.rs
@@ -1,33 +1,29 @@
-//@ build-fail
 #![feature(rustc_attrs)]
 
-#[rustc_dump_vtable]
 trait A {
     fn foo_a(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait B {
-    //~^ error vtable
     fn foo_b(&self) {}
 }
 
-#[rustc_dump_vtable]
 trait C: A + B {
-    //~^ error vtable
     fn foo_c(&self) {}
 }
 
 struct S;
 
+#[rustc_dump_vtable]
 impl A for S {}
+//~^ error vtable
+
+#[rustc_dump_vtable]
 impl B for S {}
+//~^ error vtable
+
+#[rustc_dump_vtable]
 impl C for S {}
+//~^ error vtable
 
-fn foo(c: &dyn C) {}
-fn bar(c: &dyn B) {}
-
-fn main() {
-    foo(&S);
-    bar(&S);
-}
+fn main() {}
diff --git a/tests/ui/traits/vtable/vtable-multiple.stderr b/tests/ui/traits/vtable/vtable-multiple.stderr
index 0dcd84433090..bae44148baa8 100644
--- a/tests/ui/traits/vtable/vtable-multiple.stderr
+++ b/tests/ui/traits/vtable/vtable-multiple.stderr
@@ -1,4 +1,26 @@
-error: vtable entries for ``: [
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a),
+       ]
+  --> $DIR/vtable-multiple.rs:18:1
+   |
+LL | impl A for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_b),
+       ]
+  --> $DIR/vtable-multiple.rs:22:1
+   |
+LL | impl B for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
@@ -7,21 +29,10 @@ error: vtable entries for ``: [
            TraitVPtr(),
            Method(::foo_c),
        ]
-  --> $DIR/vtable-multiple.rs:16:1
+  --> $DIR/vtable-multiple.rs:26:1
    |
-LL | trait C: A + B {
-   | ^^^^^^^^^^^^^^
+LL | impl C for S {}
+   | ^^^^^^^^^^^^
 
-error: vtable entries for ``: [
-           MetadataDropInPlace,
-           MetadataSize,
-           MetadataAlign,
-           Method(::foo_b),
-       ]
-  --> $DIR/vtable-multiple.rs:10:1
-   |
-LL | trait B {
-   | ^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/traits/vtable/vtable-vacant.rs b/tests/ui/traits/vtable/vtable-vacant.rs
index b3c768157036..b023565c56e6 100644
--- a/tests/ui/traits/vtable/vtable-vacant.rs
+++ b/tests/ui/traits/vtable/vtable-vacant.rs
@@ -1,18 +1,14 @@
-//@ build-fail
 #![feature(rustc_attrs)]
 #![feature(negative_impls)]
 
 // B --> A
 
-#[rustc_dump_vtable]
 trait A {
     fn foo_a1(&self) {}
     fn foo_a2(&self) where Self: Send {}
 }
 
-#[rustc_dump_vtable]
 trait B: A {
-    //~^ error vtable
     fn foo_b1(&self) {}
     fn foo_b2(&self) where Self: Send {}
 }
@@ -20,11 +16,12 @@ trait B: A {
 struct S;
 impl !Send for S {}
 
+#[rustc_dump_vtable]
 impl A for S {}
+//~^ error vtable
+
+#[rustc_dump_vtable]
 impl B for S {}
+//~^ error vtable
 
-fn foo(_: &dyn B) {}
-
-fn main() {
-    foo(&S);
-}
+fn main() {}
diff --git a/tests/ui/traits/vtable/vtable-vacant.stderr b/tests/ui/traits/vtable/vtable-vacant.stderr
index f6961ca010ed..ed8466f7f2ec 100644
--- a/tests/ui/traits/vtable/vtable-vacant.stderr
+++ b/tests/ui/traits/vtable/vtable-vacant.stderr
@@ -1,4 +1,16 @@
-error: vtable entries for ``: [
+error: vtable entries: [
+           MetadataDropInPlace,
+           MetadataSize,
+           MetadataAlign,
+           Method(::foo_a1),
+           Vacant,
+       ]
+  --> $DIR/vtable-vacant.rs:20:1
+   |
+LL | impl A for S {}
+   | ^^^^^^^^^^^^
+
+error: vtable entries: [
            MetadataDropInPlace,
            MetadataSize,
            MetadataAlign,
@@ -7,10 +19,10 @@ error: vtable entries for ``: [
            Method(::foo_b1),
            Vacant,
        ]
-  --> $DIR/vtable-vacant.rs:14:1
+  --> $DIR/vtable-vacant.rs:24:1
    |
-LL | trait B: A {
-   | ^^^^^^^^^^
+LL | impl B for S {}
+   | ^^^^^^^^^^^^
 
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
 

From 37a430e6ea0a674287b53a017497b3414e44b93d Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 10 Jan 2025 20:38:37 +0000
Subject: [PATCH 282/342] Remove print_vtable_sizes

---
 compiler/rustc_interface/src/passes.rs        | 91 -------------------
 compiler/rustc_session/src/code_stats.rs      | 60 +-----------
 compiler/rustc_session/src/options.rs         |  2 -
 tests/ui/traits/object/print_vtable_sizes.rs  | 62 -------------
 .../traits/object/print_vtable_sizes.stdout   | 11 ---
 5 files changed, 1 insertion(+), 225 deletions(-)
 delete mode 100644 tests/ui/traits/object/print_vtable_sizes.rs
 delete mode 100644 tests/ui/traits/object/print_vtable_sizes.stdout

diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 241bc35857a4..0b91c023cfc8 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -26,7 +26,6 @@ use rustc_parse::{
 };
 use rustc_passes::{abi_test, input_stats, layout_test};
 use rustc_resolve::Resolver;
-use rustc_session::code_stats::VTableSizeInfo;
 use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
 use rustc_session::cstore::Untracked;
 use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name};
@@ -989,90 +988,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) {
         // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally.
         let _ = tcx.all_diagnostic_items(());
     });
-
-    if sess.opts.unstable_opts.print_vtable_sizes {
-        let traits = tcx.traits(LOCAL_CRATE);
-
-        for &tr in traits {
-            if !tcx.is_dyn_compatible(tr) {
-                continue;
-            }
-
-            let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr));
-
-            let mut first_dsa = true;
-
-            // Number of vtable entries, if we didn't have upcasting
-            let mut entries_ignoring_upcasting = 0;
-            // Number of vtable entries needed solely for upcasting
-            let mut entries_for_upcasting = 0;
-
-            let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr));
-
-            // A slightly edited version of the code in
-            // `rustc_trait_selection::traits::vtable::vtable_entries`, that works without self
-            // type and just counts number of entries.
-            //
-            // Note that this is technically wrong, for traits which have associated types in
-            // supertraits:
-            //
-            //   trait A: AsRef + AsRef<()> { type T; }
-            //
-            // Without self type we can't normalize `Self::T`, so we can't know if `AsRef`
-            // and `AsRef<()>` are the same trait, thus we assume that those are different, and
-            // potentially over-estimate how many vtable entries there are.
-            //
-            // Similarly this is wrong for traits that have methods with possibly-impossible bounds.
-            // For example:
-            //
-            //   trait B { fn f(&self) where T: Copy; }
-            //
-            // Here `dyn B` will have 4 entries, while `dyn B` will only have 3.
-            // However, since we don't know `T`, we can't know if `T: Copy` holds or not,
-            // thus we lean on the bigger side and say it has 4 entries.
-            traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| {
-                match segment {
-                    traits::vtable::VtblSegment::MetadataDSA => {
-                        // If this is the first dsa, it would be included either way,
-                        // otherwise it's needed for upcasting
-                        if std::mem::take(&mut first_dsa) {
-                            entries_ignoring_upcasting += 3;
-                        } else {
-                            entries_for_upcasting += 3;
-                        }
-                    }
-
-                    traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
-                        // Lookup the shape of vtable for the trait.
-                        let own_existential_entries =
-                            tcx.own_existential_vtable_entries(trait_ref.def_id());
-
-                        // The original code here ignores the method if its predicates are
-                        // impossible. We can't really do that as, for example, all not trivial
-                        // bounds on generic parameters are impossible (since we don't know the
-                        // parameters...), see the comment above.
-                        entries_ignoring_upcasting += own_existential_entries.len();
-
-                        if emit_vptr {
-                            entries_for_upcasting += 1;
-                        }
-                    }
-                }
-
-                std::ops::ControlFlow::Continue::(())
-            });
-
-            sess.code_stats.record_vtable_size(tr, &name, VTableSizeInfo {
-                trait_name: name.clone(),
-                entries: entries_ignoring_upcasting + entries_for_upcasting,
-                entries_ignoring_upcasting,
-                entries_for_upcasting,
-                upcasting_cost_percent: entries_for_upcasting as f64
-                    / entries_ignoring_upcasting as f64
-                    * 100.,
-            })
-        }
-    }
 }
 
 /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used
@@ -1153,12 +1068,6 @@ pub(crate) fn start_codegen<'tcx>(
         tcx.sess.code_stats.print_type_sizes();
     }
 
-    if tcx.sess.opts.unstable_opts.print_vtable_sizes {
-        let crate_name = tcx.crate_name(LOCAL_CRATE);
-
-        tcx.sess.code_stats.print_vtable_sizes(crate_name);
-    }
-
     codegen
 }
 
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index f3c21992784c..b4597ae2515d 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -1,10 +1,9 @@
 use std::cmp;
 
 use rustc_abi::{Align, Size};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lock;
 use rustc_span::Symbol;
-use rustc_span::def_id::DefId;
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct VariantInfo {
@@ -71,29 +70,9 @@ pub struct TypeSizeInfo {
     pub variants: Vec,
 }
 
-pub struct VTableSizeInfo {
-    pub trait_name: String,
-
-    /// Number of entries in a vtable with the current algorithm
-    /// (i.e. with upcasting).
-    pub entries: usize,
-
-    /// Number of entries in a vtable, as-if we did not have trait upcasting.
-    pub entries_ignoring_upcasting: usize,
-
-    /// Number of entries in a vtable needed solely for upcasting
-    /// (i.e. `entries - entries_ignoring_upcasting`).
-    pub entries_for_upcasting: usize,
-
-    /// Cost of having upcasting in % relative to the number of entries without
-    /// upcasting (i.e. `entries_for_upcasting / entries_ignoring_upcasting * 100%`).
-    pub upcasting_cost_percent: f64,
-}
-
 #[derive(Default)]
 pub struct CodeStats {
     type_sizes: Lock>,
-    vtable_sizes: Lock>,
 }
 
 impl CodeStats {
@@ -127,14 +106,6 @@ impl CodeStats {
         self.type_sizes.borrow_mut().insert(info);
     }
 
-    pub fn record_vtable_size(&self, trait_did: DefId, trait_name: &str, info: VTableSizeInfo) {
-        let prev = self.vtable_sizes.lock().insert(trait_did, info);
-        assert!(
-            prev.is_none(),
-            "size of vtable for `{trait_name}` ({trait_did:?}) is already recorded"
-        );
-    }
-
     pub fn print_type_sizes(&self) {
         let type_sizes = self.type_sizes.borrow();
         // We will soon sort, so the initial order does not matter.
@@ -238,33 +209,4 @@ impl CodeStats {
             }
         }
     }
-
-    pub fn print_vtable_sizes(&self, crate_name: Symbol) {
-        // We will soon sort, so the initial order does not matter.
-        #[allow(rustc::potential_query_instability)]
-        let mut infos =
-            std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::>();
-
-        // Primary sort: cost % in reverse order (from largest to smallest)
-        // Secondary sort: trait_name
-        infos.sort_by(|a, b| {
-            a.upcasting_cost_percent
-                .total_cmp(&b.upcasting_cost_percent)
-                .reverse()
-                .then_with(|| a.trait_name.cmp(&b.trait_name))
-        });
-
-        for VTableSizeInfo {
-            trait_name,
-            entries,
-            entries_ignoring_upcasting,
-            entries_for_upcasting,
-            upcasting_cost_percent,
-        } in infos
-        {
-            println!(
-                r#"print-vtable-sizes {{ "crate_name": "{crate_name}", "trait_name": "{trait_name}", "entries": "{entries}", "entries_ignoring_upcasting": "{entries_ignoring_upcasting}", "entries_for_upcasting": "{entries_for_upcasting}", "upcasting_cost_percent": "{upcasting_cost_percent}" }}"#
-            );
-        }
-    }
 }
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 4ce638251297..b7022f098e2c 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -2033,8 +2033,6 @@ options! {
          Note that this overwrites the effect `-Clink-dead-code` has on collection!"),
     print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
         "print layout information for each type encountered (default: no)"),
-    print_vtable_sizes: bool = (false, parse_bool, [UNTRACKED],
-        "print size comparison between old and new vtable layouts (default: no)"),
     proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
          "show backtraces for panics during proc-macro execution (default: no)"),
     proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread,
diff --git a/tests/ui/traits/object/print_vtable_sizes.rs b/tests/ui/traits/object/print_vtable_sizes.rs
deleted file mode 100644
index 2b1745da5f30..000000000000
--- a/tests/ui/traits/object/print_vtable_sizes.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-//@ check-pass
-//@ compile-flags: -Z print-vtable-sizes
-#![crate_type = "lib"]
-
-trait A: AsRef<[T::V]> + AsMut<[T::V]> {}
-
-trait B: AsRef + AsRef + AsRef + AsRef {}
-
-trait C {
-    fn x() {} // not dyn-compatible, shouldn't be reported
-}
-
-// This does not have any upcasting cost,
-// because both `Send` and `Sync` are traits
-// with no methods
-trait D: Send + Sync + help::MarkerWithSuper {}
-
-// This can't have no cost without reordering,
-// because `Super::f`.
-trait E: help::MarkerWithSuper + Send + Sync {}
-
-trait F {
-    fn a(&self);
-    fn b(&self);
-    fn c(&self);
-
-    fn d() -> Self
-    where
-        Self: Sized;
-}
-
-trait G: AsRef + AsRef + help::MarkerWithSuper {
-    fn a(&self);
-    fn b(&self);
-    fn c(&self);
-    fn d(&self);
-    fn e(&self);
-
-    fn f() -> Self
-    where
-        Self: Sized;
-}
-
-// Traits with the same name
-const _: () = {
-    trait S {}
-};
-const _: () = {
-    trait S {}
-};
-
-mod help {
-    pub trait V {
-        type V;
-    }
-
-    pub trait MarkerWithSuper: Super {}
-
-    pub trait Super {
-        fn f(&self);
-    }
-}
diff --git a/tests/ui/traits/object/print_vtable_sizes.stdout b/tests/ui/traits/object/print_vtable_sizes.stdout
deleted file mode 100644
index 4daf47695765..000000000000
--- a/tests/ui/traits/object/print_vtable_sizes.stdout
+++ /dev/null
@@ -1,11 +0,0 @@
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "G", "entries": "13", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "2", "upcasting_cost_percent": "18.181818181818183" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "D", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "E", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::MarkerWithSuper", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::Super", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
-print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::V", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }

From fdc4bd22b7b8117f4a3864c342773df600f5b956 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 10 Jan 2025 04:36:11 +0000
Subject: [PATCH 283/342] Do not treat vtable supertraits as distinct when
 bound with different bound vars

---
 .../rustc_codegen_cranelift/src/constant.rs   |   5 +-
 compiler/rustc_codegen_gcc/src/common.rs      |   7 +-
 compiler/rustc_codegen_llvm/src/common.rs     |   7 +-
 .../src/debuginfo/metadata.rs                 |   2 +-
 compiler/rustc_codegen_ssa/src/meth.rs        |  14 +-
 .../rustc_const_eval/src/interpret/cast.rs    |  10 +-
 .../rustc_const_eval/src/interpret/traits.rs  |   3 +-
 .../rustc_hir_analysis/src/collect/dump.rs    |   4 +-
 compiler/rustc_middle/src/query/keys.rs       |   2 +-
 compiler/rustc_middle/src/query/mod.rs        |   6 +-
 compiler/rustc_middle/src/ty/layout.rs        |   7 +-
 compiler/rustc_middle/src/ty/vtable.rs        |  17 ++-
 compiler/rustc_monomorphize/src/collector.rs  |  14 +-
 compiler/rustc_smir/src/rustc_smir/context.rs |   4 +-
 .../src/traits/vtable.rs                      | 132 ++++++++----------
 ...le-supertraits-modulo-binder-vtable.stderr |   2 -
 .../multiple-supertraits-modulo-binder.rs     |  24 ++++
 ...tiple-supertraits-modulo-binder.run.stdout |   1 +
 18 files changed, 146 insertions(+), 115 deletions(-)
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout

diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 3e7b81a96b68..05ba53430a8a 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -245,7 +245,10 @@ pub(crate) fn data_id_for_vtable<'tcx>(
     ty: Ty<'tcx>,
     trait_ref: Option>>,
 ) -> DataId {
-    let alloc_id = tcx.vtable_allocation((ty, trait_ref));
+    let alloc_id = tcx.vtable_allocation((
+        ty,
+        trait_ref.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
+    ));
     data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
 }
 
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index bd5d6ba387cf..20a3482aaa27 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -234,7 +234,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
                     GlobalAlloc::VTable(ty, dyn_ty) => {
                         let alloc = self
                             .tcx
-                            .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal())))
+                            .global_alloc(self.tcx.vtable_allocation((
+                                ty,
+                                dyn_ty.principal().map(|principal| {
+                                    self.tcx.instantiate_bound_regions_with_erased(principal)
+                                }),
+                            )))
                             .unwrap_memory();
                         let init = const_alloc_to_gcc(self, alloc);
                         self.static_addr_of(init, alloc.inner().align, None)
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index b4e9b9f44f4a..8908ae314b8c 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -312,7 +312,12 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                     GlobalAlloc::VTable(ty, dyn_ty) => {
                         let alloc = self
                             .tcx
-                            .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal())))
+                            .global_alloc(self.tcx.vtable_allocation((
+                                ty,
+                                dyn_ty.principal().map(|principal| {
+                                    self.tcx.instantiate_bound_regions_with_erased(principal)
+                                }),
+                            )))
                             .unwrap_memory();
                         let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
                         let value = self.static_addr_of(init, alloc.inner().align, None);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 8d782a618fc0..587cb1bef2bd 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -1406,7 +1406,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
 
     let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
         let trait_ref = poly_trait_ref.with_self_ty(tcx, ty);
-        let trait_ref = tcx.erase_regions(trait_ref);
+        let trait_ref = tcx.erase_regions(tcx.instantiate_bound_regions_with_erased(trait_ref));
 
         tcx.vtable_entries(trait_ref)
     } else {
diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs
index 64cd4c38937e..c51a57be71ce 100644
--- a/compiler/rustc_codegen_ssa/src/meth.rs
+++ b/compiler/rustc_codegen_ssa/src/meth.rs
@@ -96,24 +96,28 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option> {
 pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
     cx: &Cx,
     ty: Ty<'tcx>,
-    trait_ref: Option>,
+    poly_trait_ref: Option>,
 ) -> Cx::Value {
     let tcx = cx.tcx();
 
     // Check the cache.
-    if let Some(&val) = cx.vtables().borrow().get(&(ty, trait_ref)) {
+    if let Some(&val) = cx.vtables().borrow().get(&(ty, poly_trait_ref)) {
         return val;
     }
 
+    // FIXME(trait_upcasting): Take a non-higher-ranked vtable as arg.
+    let trait_ref =
+        poly_trait_ref.map(|trait_ref| tcx.instantiate_bound_regions_with_erased(trait_ref));
+
     let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref));
     let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory();
     let vtable_const = cx.const_data_from_alloc(vtable_allocation);
     let align = cx.data_layout().pointer_align.abi;
     let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
 
-    cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable);
-    cx.create_vtable_debuginfo(ty, trait_ref, vtable);
-    cx.vtables().borrow_mut().insert((ty, trait_ref), vtable);
+    cx.apply_vcall_visibility_metadata(ty, poly_trait_ref, vtable);
+    cx.create_vtable_debuginfo(ty, poly_trait_ref, vtable);
+    cx.vtables().borrow_mut().insert((ty, poly_trait_ref), vtable);
     vtable
 }
 
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index ef3e96784ce8..b05efd10e666 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -419,8 +419,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
                     let vtable_entries = self.vtable_entries(data_a.principal(), ty);
                     if let Some(entry_idx) = vptr_entry_idx {
-                        let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
-                            vtable_entries.get(entry_idx)
+                        let Some(&ty::VtblEntry::TraitVPtr(_)) = vtable_entries.get(entry_idx)
                         else {
                             span_bug!(
                                 self.cur_span(),
@@ -429,13 +428,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                                 dest_pointee_ty
                             );
                         };
-                        let erased_trait_ref = upcast_trait_ref
-                            .map_bound(|r| ty::ExistentialTraitRef::erase_self_ty(*self.tcx, r));
-                        assert!(
-                            data_b
-                                .principal()
-                                .is_some_and(|b| self.eq_in_param_env(erased_trait_ref, b))
-                        );
                     } else {
                         // In this case codegen would keep using the old vtable. We don't want to do
                         // that as it has the wrong trait. The reason codegen can do this is that
diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs
index af8d618b6b5e..4cfaacebfcd0 100644
--- a/compiler/rustc_const_eval/src/interpret/traits.rs
+++ b/compiler/rustc_const_eval/src/interpret/traits.rs
@@ -54,7 +54,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ) -> &'tcx [VtblEntry<'tcx>] {
         if let Some(trait_) = trait_ {
             let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty);
-            let trait_ref = self.tcx.erase_regions(trait_ref);
+            let trait_ref =
+                self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
             self.tcx.vtable_entries(trait_ref)
         } else {
             TyCtxt::COMMON_VTABLE_ENTRIES
diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs
index d990d824200a..4a508fc0cf6a 100644
--- a/compiler/rustc_hir_analysis/src/collect/dump.rs
+++ b/compiler/rustc_hir_analysis/src/collect/dump.rs
@@ -123,7 +123,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
                     );
                     continue;
                 };
-                tcx.vtable_entries(ty::Binder::dummy(trait_ref))
+                tcx.vtable_entries(trait_ref)
             }
             hir::ItemKind::TyAlias(_, _) => {
                 let ty = tcx.type_of(def_id).instantiate_identity();
@@ -149,7 +149,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) {
                 };
                 if let Some(principal) = data.principal() {
                     tcx.vtable_entries(
-                        principal.map_bound(|principal| principal.with_self_ty(tcx, ty)),
+                        tcx.instantiate_bound_regions_with_erased(principal).with_self_ty(tcx, ty),
                     )
                 } else {
                     TyCtxt::COMMON_VTABLE_ENTRIES
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index e243425c0b7b..03fe0a300c08 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -95,7 +95,7 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
     }
 }
 
-impl<'tcx> Key for (Ty<'tcx>, Option>) {
+impl<'tcx> Key for (Ty<'tcx>, Option>) {
     type Cache = DefaultCache;
 
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d83bc19a6a2f..6b6b4648764a 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1437,9 +1437,9 @@ rustc_queries! {
         desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) }
     }
 
-    query vtable_entries(key: ty::PolyTraitRef<'tcx>)
+    query vtable_entries(key: ty::TraitRef<'tcx>)
                         -> &'tcx [ty::VtblEntry<'tcx>] {
-        desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id()) }
+        desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) }
     }
 
     query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize {
@@ -1451,7 +1451,7 @@ rustc_queries! {
             key.1, key.0 }
     }
 
-    query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId {
+    query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId {
         desc { |tcx| "vtable const allocation for <{} as {}>",
             key.0,
             key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned())
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 0d41a1d7dbfb..d0b78a89062c 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -855,7 +855,12 @@ where
                     }
 
                     let mk_dyn_vtable = |principal: Option>| {
-                        let min_count = ty::vtable_min_entries(tcx, principal);
+                        let min_count = ty::vtable_min_entries(
+                            tcx,
+                            principal.map(|principal| {
+                                tcx.instantiate_bound_regions_with_erased(principal)
+                            }),
+                        );
                         Ty::new_imm_ref(
                             tcx,
                             tcx.lifetimes.re_static,
diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs
index 455bd16ff8c2..6c9e0e7c0eb8 100644
--- a/compiler/rustc_middle/src/ty/vtable.rs
+++ b/compiler/rustc_middle/src/ty/vtable.rs
@@ -7,7 +7,7 @@ use rustc_type_ir::elaborate;
 use crate::mir::interpret::{
     AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range,
 };
-use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt};
+use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt};
 
 #[derive(Clone, Copy, PartialEq, HashStable)]
 pub enum VtblEntry<'tcx> {
@@ -22,7 +22,7 @@ pub enum VtblEntry<'tcx> {
     /// dispatchable associated function
     Method(Instance<'tcx>),
     /// pointer to a separate supertrait vtable, can be used by trait upcasting coercion
-    TraitVPtr(PolyTraitRef<'tcx>),
+    TraitVPtr(TraitRef<'tcx>),
 }
 
 impl<'tcx> fmt::Debug for VtblEntry<'tcx> {
@@ -59,7 +59,7 @@ pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
 // function is an accurate approximation. We verify this when actually computing the vtable below.
 pub(crate) fn vtable_min_entries<'tcx>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: Option>,
+    trait_ref: Option>,
 ) -> usize {
     let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len();
     let Some(trait_ref) = trait_ref else {
@@ -67,7 +67,7 @@ pub(crate) fn vtable_min_entries<'tcx>(
     };
 
     // This includes self in supertraits.
-    for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id()) {
+    for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id) {
         count += tcx.own_existential_vtable_entries(def_id).len();
     }
 
@@ -83,7 +83,7 @@ pub(crate) fn vtable_min_entries<'tcx>(
 /// initial contents.)
 pub(super) fn vtable_allocation_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
-    key: (Ty<'tcx>, Option>),
+    key: (Ty<'tcx>, Option>),
 ) -> AllocId {
     let (ty, poly_trait_ref) = key;
 
@@ -118,7 +118,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
 
     for (idx, entry) in vtable_entries.iter().enumerate() {
         let idx: u64 = u64::try_from(idx).unwrap();
-        let scalar = match entry {
+        let scalar = match *entry {
             VtblEntry::MetadataDropInPlace => {
                 if ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) {
                     let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
@@ -134,13 +134,12 @@ pub(super) fn vtable_allocation_provider<'tcx>(
             VtblEntry::Vacant => continue,
             VtblEntry::Method(instance) => {
                 // Prepare the fn ptr we write into the vtable.
-                let fn_alloc_id = tcx.reserve_and_set_fn_alloc(*instance, CTFE_ALLOC_SALT);
+                let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT);
                 let fn_ptr = Pointer::from(fn_alloc_id);
                 Scalar::from_pointer(fn_ptr, &tcx)
             }
             VtblEntry::TraitVPtr(trait_ref) => {
-                let super_trait_ref = trait_ref
-                    .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
+                let super_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
                 let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref)));
                 let vptr = Pointer::from(supertrait_alloc_id);
                 Scalar::from_pointer(vptr, &tcx)
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index d53848f7461f..150594ab94d9 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1138,11 +1138,12 @@ fn create_mono_items_for_vtable_methods<'tcx>(
         bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type");
     };
     if let Some(principal) = trait_ty.principal() {
-        let poly_trait_ref = principal.with_self_ty(tcx, impl_ty);
-        assert!(!poly_trait_ref.has_escaping_bound_vars());
+        let trait_ref =
+            tcx.instantiate_bound_regions_with_erased(principal.with_self_ty(tcx, impl_ty));
+        assert!(!trait_ref.has_escaping_bound_vars());
 
         // Walk all methods of the trait, including those of its supertraits
-        let entries = tcx.vtable_entries(poly_trait_ref);
+        let entries = tcx.vtable_entries(trait_ref);
         debug!(?entries);
         let methods = entries
             .iter()
@@ -1197,7 +1198,12 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt
             }
         }
         GlobalAlloc::VTable(ty, dyn_ty) => {
-            let alloc_id = tcx.vtable_allocation((ty, dyn_ty.principal()));
+            let alloc_id = tcx.vtable_allocation((
+                ty,
+                dyn_ty
+                    .principal()
+                    .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
+            ));
             collect_alloc(tcx, alloc_id, output)
         }
     }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 31c7e6c3eb44..6313fac5aeab 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -746,7 +746,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let tcx = tables.tcx;
         let alloc_id = tables.tcx.vtable_allocation((
             ty.internal(&mut *tables, tcx),
-            trait_ref.internal(&mut *tables, tcx),
+            trait_ref
+                .internal(&mut *tables, tcx)
+                .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
         ));
         Some(alloc_id.stable(&mut *tables))
     }
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index 72ddf01c7913..b23d1da16084 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -2,8 +2,8 @@ use std::fmt::Debug;
 use std::ops::ControlFlow;
 
 use rustc_hir::def_id::DefId;
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::infer::at::ToTrace;
-use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt};
 use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::util::PredicateSet;
 use rustc_middle::bug;
@@ -20,7 +20,7 @@ use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method
 #[derive(Clone, Debug)]
 pub enum VtblSegment<'tcx> {
     MetadataDSA,
-    TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
+    TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool },
 }
 
 /// Prepare the segments for a vtable
@@ -28,7 +28,7 @@ pub enum VtblSegment<'tcx> {
 // about our `Self` type here.
 pub fn prepare_vtable_segments<'tcx, T>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
     segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow,
 ) -> Option {
     prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
@@ -38,7 +38,7 @@ pub fn prepare_vtable_segments<'tcx, T>(
 /// such that we can use `?` in the body.
 fn prepare_vtable_segments_inner<'tcx, T>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
     mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow,
 ) -> ControlFlow {
     // The following constraints holds for the final arrangement.
@@ -91,7 +91,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
     let mut emit_vptr_on_new_entry = false;
     let mut visited = PredicateSet::new(tcx);
     let predicate = trait_ref.upcast(tcx);
-    let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
+    let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> =
         smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
     visited.insert(predicate);
 
@@ -124,10 +124,19 @@ fn prepare_vtable_segments_inner<'tcx, T>(
             let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
 
             let mut direct_super_traits_iter = tcx
-                .explicit_super_predicates_of(inner_most_trait_ref.def_id())
+                .explicit_super_predicates_of(inner_most_trait_ref.def_id)
                 .iter_identity_copied()
                 .filter_map(move |(pred, _)| {
-                    pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause()
+                    Some(
+                        tcx.instantiate_bound_regions_with_erased(
+                            pred.instantiate_supertrait(
+                                tcx,
+                                ty::Binder::dummy(inner_most_trait_ref),
+                            )
+                            .as_trait_clause()?,
+                        )
+                        .trait_ref,
+                    )
                 });
 
             // Find an unvisited supertrait
@@ -135,16 +144,11 @@ fn prepare_vtable_segments_inner<'tcx, T>(
                 .find(|&super_trait| visited.insert(super_trait.upcast(tcx)))
             {
                 // Push it to the stack for the next iteration of 'diving_in to pick up
-                Some(unvisited_super_trait) => {
-                    // We're throwing away potential constness of super traits here.
-                    // FIXME: handle ~const super traits
-                    let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref);
-                    stack.push((
-                        next_super_trait,
-                        emit_vptr_on_new_entry,
-                        maybe_iter(Some(direct_super_traits_iter)),
-                    ))
-                }
+                Some(next_super_trait) => stack.push((
+                    next_super_trait,
+                    emit_vptr_on_new_entry,
+                    maybe_iter(Some(direct_super_traits_iter)),
+                )),
 
                 // There are no more unvisited direct super traits, dive-in finished
                 None => break 'diving_in,
@@ -153,8 +157,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
 
         // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
         while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
-            let has_entries =
-                has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id());
+            let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id);
 
             segment_visitor(VtblSegment::TraitOwnEntries {
                 trait_ref: inner_most_trait_ref,
@@ -168,11 +171,6 @@ fn prepare_vtable_segments_inner<'tcx, T>(
             if let Some(next_inner_most_trait_ref) =
                 siblings.find(|&sibling| visited.insert(sibling.upcast(tcx)))
             {
-                // We're throwing away potential constness of super traits here.
-                // FIXME: handle ~const super traits
-                let next_inner_most_trait_ref =
-                    next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
-
                 stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
 
                 // just pushed a new trait onto the stack, so we need to go through its super traits
@@ -229,7 +227,7 @@ fn own_existential_vtable_entries_iter(
 /// that come from `trait_ref`, including its supertraits.
 fn vtable_entries<'tcx>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
 ) -> &'tcx [VtblEntry<'tcx>] {
     debug!("vtable_entries({:?})", trait_ref);
 
@@ -241,33 +239,26 @@ fn vtable_entries<'tcx>(
                 entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
             }
             VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
-                let existential_trait_ref = trait_ref
-                    .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
+                let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
 
                 // Lookup the shape of vtable for the trait.
                 let own_existential_entries =
-                    tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
+                    tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
 
                 let own_entries = own_existential_entries.iter().copied().map(|def_id| {
                     debug!("vtable_entries: trait_method={:?}", def_id);
 
                     // The method may have some early-bound lifetimes; add regions for those.
-                    let args = trait_ref.map_bound(|trait_ref| {
+                    // FIXME: Is this normalize needed?
+                    let args = tcx.normalize_erasing_regions(
+                        ty::TypingEnv::fully_monomorphized(),
                         GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
                             GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
                             GenericParamDefKind::Type { .. }
                             | GenericParamDefKind::Const { .. } => {
                                 trait_ref.args[param.index as usize]
                             }
-                        })
-                    });
-
-                    // The trait type may have higher-ranked lifetimes in it;
-                    // erase them if they appear, so that we get the type
-                    // at some particular call site.
-                    let args = tcx.normalize_erasing_late_bound_regions(
-                        ty::TypingEnv::fully_monomorphized(),
-                        args,
+                        }),
                     );
 
                     // It's possible that the method relies on where-clauses that
@@ -318,10 +309,11 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
     let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
         bug!();
     };
-    let source_principal =
-        source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
+    let source_principal = tcx.instantiate_bound_regions_with_erased(
+        source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self),
+    );
 
-    let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key));
+    let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
 
     let vtable_segment_callback = {
         let mut vptr_offset = 0;
@@ -333,15 +325,14 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
                 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
                     if trait_refs_are_compatible(
                         tcx,
-                        vtable_principal
-                            .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
+                        ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
                         target_principal,
                     ) {
                         return ControlFlow::Break(vptr_offset);
                     }
 
                     vptr_offset +=
-                        tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
+                        tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
 
                     if emit_vptr {
                         vptr_offset += 1;
@@ -373,14 +364,15 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
     let ty::Dynamic(target, _, _) = *target.kind() else {
         bug!();
     };
-    let target_principal = target.principal()?;
+    let target_principal = tcx.instantiate_bound_regions_with_erased(target.principal()?);
 
     // Given that we have a target principal, it is a bug for there not to be a source principal.
     let ty::Dynamic(source, _, _) = *source.kind() else {
         bug!();
     };
-    let source_principal =
-        source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
+    let source_principal = tcx.instantiate_bound_regions_with_erased(
+        source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self),
+    );
 
     let vtable_segment_callback = {
         let mut vptr_offset = 0;
@@ -391,11 +383,10 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
                 }
                 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
                     vptr_offset +=
-                        tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
+                        tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
                     if trait_refs_are_compatible(
                         tcx,
-                        vtable_principal
-                            .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
+                        ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
                         target_principal,
                     ) {
                         if emit_vptr {
@@ -419,37 +410,32 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
 
 fn trait_refs_are_compatible<'tcx>(
     tcx: TyCtxt<'tcx>,
-    hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>,
-    hr_target_principal: ty::PolyExistentialTraitRef<'tcx>,
+    vtable_principal: ty::ExistentialTraitRef<'tcx>,
+    target_principal: ty::ExistentialTraitRef<'tcx>,
 ) -> bool {
-    if hr_vtable_principal.def_id() != hr_target_principal.def_id() {
+    if vtable_principal.def_id != target_principal.def_id {
         return false;
     }
 
     let (infcx, param_env) =
         tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
     let ocx = ObligationCtxt::new(&infcx);
-    let hr_source_principal =
-        ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal);
-    let hr_target_principal =
-        ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal);
-    infcx.enter_forall(hr_target_principal, |target_principal| {
-        let source_principal = infcx.instantiate_binder_with_fresh_vars(
-            DUMMY_SP,
-            BoundRegionConversionTime::HigherRankedType,
-            hr_source_principal,
-        );
-        let Ok(()) = ocx.eq_trace(
+    let source_principal = ocx.normalize(&ObligationCause::dummy(), param_env, vtable_principal);
+    let target_principal = ocx.normalize(&ObligationCause::dummy(), param_env, target_principal);
+    let Ok(()) = ocx.eq_trace(
+        &ObligationCause::dummy(),
+        param_env,
+        ToTrace::to_trace(
             &ObligationCause::dummy(),
-            param_env,
-            ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal),
-            target_principal,
-            source_principal,
-        ) else {
-            return false;
-        };
-        ocx.select_all_or_error().is_empty()
-    })
+            ty::Binder::dummy(target_principal),
+            ty::Binder::dummy(source_principal),
+        ),
+        target_principal,
+        source_principal,
+    ) else {
+        return false;
+    };
+    ocx.select_all_or_error().is_empty()
 }
 
 pub(super) fn provide(providers: &mut Providers) {
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
index 1b99be4ab880..4bb7e1bdb6ad 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
@@ -3,8 +3,6 @@ error: vtable entries: [
            MetadataSize,
            MetadataAlign,
            Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
-           Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
-           TraitVPtr(for<'a>  Trait<&(), &'a ()> as Supertrait<&'a ()>>),
            Method( Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)),
        ]
   --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs
new file mode 100644
index 000000000000..7bc069f4f440
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs
@@ -0,0 +1,24 @@
+//@ run-pass
+//@ check-run-results
+
+#![feature(trait_upcasting)]
+
+trait Supertrait {
+    fn _print_numbers(&self, mem: &[usize; 100]) {
+        println!("{mem:?}");
+    }
+}
+impl Supertrait for () {}
+
+trait Trait: Supertrait + Supertrait {
+    fn say_hello(&self, _: &usize) {
+        println!("Hello!");
+    }
+}
+impl Trait for () {}
+
+fn main() {
+    (&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
+        as &'static dyn Trait<&'static (), &'static ()>)
+        .say_hello(&0);
+}
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout
new file mode 100644
index 000000000000..10ddd6d257e0
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout
@@ -0,0 +1 @@
+Hello!

From 739ef83f318defbb9692029fa98f56639896c6fd Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 10 Jan 2025 04:47:45 +0000
Subject: [PATCH 284/342] Normalize vtable entries before walking and
 deduplicating them

---
 .../rustc_const_eval/src/interpret/cast.rs    |  9 ++++-
 compiler/rustc_infer/src/infer/at.rs          | 24 ++++++++++++++
 .../src/traits/vtable.rs                      | 33 +++++++------------
 ...rtraits-modulo-normalization-vtable.stderr |  2 --
 ...ltiple-supertraits-modulo-normalization.rs | 32 ++++++++++++++++++
 ...upertraits-modulo-normalization.run.stdout |  1 +
 6 files changed, 77 insertions(+), 24 deletions(-)
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs
 create mode 100644 tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout

diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index b05efd10e666..52bc2af928dc 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -419,7 +419,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
                     let vtable_entries = self.vtable_entries(data_a.principal(), ty);
                     if let Some(entry_idx) = vptr_entry_idx {
-                        let Some(&ty::VtblEntry::TraitVPtr(_)) = vtable_entries.get(entry_idx)
+                        let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
+                            vtable_entries.get(entry_idx)
                         else {
                             span_bug!(
                                 self.cur_span(),
@@ -428,6 +429,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                                 dest_pointee_ty
                             );
                         };
+                        let erased_trait_ref =
+                            ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
+                        assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
+                            erased_trait_ref,
+                            self.tcx.instantiate_bound_regions_with_erased(b)
+                        )));
                     } else {
                         // In this case codegen would keep using the old vtable. We don't want to do
                         // that as it has the wrong trait. The reason codegen can do this is that
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 12e2bbc968f0..ad15b764bcc3 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -402,6 +402,18 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> {
     }
 }
 
+impl<'tcx> ToTrace<'tcx> for ty::ExistentialTraitRef<'tcx> {
+    fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
+        TypeTrace {
+            cause: cause.clone(),
+            values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(
+                ty::Binder::dummy(a),
+                ty::Binder::dummy(b),
+            )),
+        }
+    }
+}
+
 impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> {
     fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
         TypeTrace {
@@ -410,3 +422,15 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> {
         }
     }
 }
+
+impl<'tcx> ToTrace<'tcx> for ty::ExistentialProjection<'tcx> {
+    fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> {
+        TypeTrace {
+            cause: cause.clone(),
+            values: ValuePairs::ExistentialProjection(ExpectedFound::new(
+                ty::Binder::dummy(a),
+                ty::Binder::dummy(b),
+            )),
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index b23d1da16084..abdf5df6f728 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -3,7 +3,6 @@ use std::ops::ControlFlow;
 
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
-use rustc_infer::infer::at::ToTrace;
 use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::util::PredicateSet;
 use rustc_middle::bug;
@@ -127,16 +126,15 @@ fn prepare_vtable_segments_inner<'tcx, T>(
                 .explicit_super_predicates_of(inner_most_trait_ref.def_id)
                 .iter_identity_copied()
                 .filter_map(move |(pred, _)| {
-                    Some(
-                        tcx.instantiate_bound_regions_with_erased(
-                            pred.instantiate_supertrait(
-                                tcx,
-                                ty::Binder::dummy(inner_most_trait_ref),
-                            )
-                            .as_trait_clause()?,
-                        )
-                        .trait_ref,
+                    pred.instantiate_supertrait(tcx, ty::Binder::dummy(inner_most_trait_ref))
+                        .as_trait_clause()
+                })
+                .map(move |pred| {
+                    tcx.normalize_erasing_late_bound_regions(
+                        ty::TypingEnv::fully_monomorphized(),
+                        pred,
                     )
+                    .trait_ref
                 });
 
             // Find an unvisited supertrait
@@ -229,6 +227,8 @@ fn vtable_entries<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
 ) -> &'tcx [VtblEntry<'tcx>] {
+    debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
+
     debug!("vtable_entries({:?})", trait_ref);
 
     let mut entries = vec![];
@@ -422,17 +422,8 @@ fn trait_refs_are_compatible<'tcx>(
     let ocx = ObligationCtxt::new(&infcx);
     let source_principal = ocx.normalize(&ObligationCause::dummy(), param_env, vtable_principal);
     let target_principal = ocx.normalize(&ObligationCause::dummy(), param_env, target_principal);
-    let Ok(()) = ocx.eq_trace(
-        &ObligationCause::dummy(),
-        param_env,
-        ToTrace::to_trace(
-            &ObligationCause::dummy(),
-            ty::Binder::dummy(target_principal),
-            ty::Binder::dummy(source_principal),
-        ),
-        target_principal,
-        source_principal,
-    ) else {
+    let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target_principal, source_principal)
+    else {
         return false;
     };
     ocx.select_all_or_error().is_empty()
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
index 9e93e1c48c9e..04b1afae7bec 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
@@ -3,8 +3,6 @@ error: vtable entries: [
            MetadataSize,
            MetadataAlign,
            Method(<() as Supertrait<()>>::_print_numbers),
-           Method(<() as Supertrait<()>>::_print_numbers),
-           TraitVPtr(<() as Supertrait<<() as Identity>::Selff>>),
            Method(<() as Middle<()>>::say_hello),
        ]
   --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs
new file mode 100644
index 000000000000..fd0f62b4255a
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs
@@ -0,0 +1,32 @@
+//@ run-pass
+//@ check-run-results
+
+#![feature(trait_upcasting)]
+
+trait Supertrait {
+    fn _print_numbers(&self, mem: &[usize; 100]) {
+        println!("{mem:?}");
+    }
+}
+impl Supertrait for () {}
+
+trait Identity {
+    type Selff;
+}
+impl Identity for Selff {
+    type Selff = Selff;
+}
+
+trait Middle: Supertrait<()> + Supertrait {
+    fn say_hello(&self, _: &usize) {
+        println!("Hello!");
+    }
+}
+impl Middle for () {}
+
+trait Trait: Middle<<() as Identity>::Selff> {}
+impl Trait for () {}
+
+fn main() {
+    (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
+}
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout
new file mode 100644
index 000000000000..10ddd6d257e0
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout
@@ -0,0 +1 @@
+Hello!

From 9dc41a048d7dc765b28102d557eec40ff050e6ab Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 10 Jan 2025 20:26:10 +0000
Subject: [PATCH 285/342] Use ExistentialTraitRef throughout codegen

---
 .../rustc_codegen_cranelift/src/constant.rs   | 25 ++++++++++-------
 .../rustc_codegen_cranelift/src/unsize.rs     |  7 ++++-
 .../rustc_codegen_cranelift/src/vtable.rs     |  2 +-
 compiler/rustc_codegen_gcc/src/context.rs     |  6 ++---
 compiler/rustc_codegen_gcc/src/debuginfo.rs   |  4 +--
 compiler/rustc_codegen_llvm/src/context.rs    |  8 +++---
 .../src/debuginfo/metadata.rs                 | 12 ++++-----
 .../src/debuginfo/metadata/type_map.rs        |  6 ++---
 .../rustc_codegen_llvm/src/debuginfo/mod.rs   |  2 +-
 compiler/rustc_codegen_ssa/src/base.rs        | 15 +++++------
 .../src/debuginfo/type_names.rs               |  6 ++---
 compiler/rustc_codegen_ssa/src/meth.rs        | 27 ++++++++++---------
 .../rustc_codegen_ssa/src/traits/debuginfo.rs |  4 +--
 compiler/rustc_codegen_ssa/src/traits/misc.rs |  4 +--
 compiler/rustc_symbol_mangling/src/lib.rs     |  2 +-
 compiler/rustc_symbol_mangling/src/v0.rs      |  4 +--
 16 files changed, 71 insertions(+), 63 deletions(-)

diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 05ba53430a8a..425b2adf32a3 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -6,7 +6,7 @@ use cranelift_module::*;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint};
-use rustc_middle::ty::{Binder, ExistentialTraitRef, ScalarInt};
+use rustc_middle::ty::{ExistentialTraitRef, ScalarInt};
 
 use crate::prelude::*;
 
@@ -167,7 +167,9 @@ pub(crate) fn codegen_const_value<'tcx>(
                             &mut fx.constants_cx,
                             fx.module,
                             ty,
-                            dyn_ty.principal(),
+                            dyn_ty.principal().map(|principal| {
+                                fx.tcx.instantiate_bound_regions_with_erased(principal)
+                            }),
                         );
                         let local_data_id =
                             fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
@@ -243,12 +245,9 @@ pub(crate) fn data_id_for_vtable<'tcx>(
     cx: &mut ConstantCx,
     module: &mut dyn Module,
     ty: Ty<'tcx>,
-    trait_ref: Option>>,
+    trait_ref: Option>,
 ) -> DataId {
-    let alloc_id = tcx.vtable_allocation((
-        ty,
-        trait_ref.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
-    ));
+    let alloc_id = tcx.vtable_allocation((ty, trait_ref));
     data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
 }
 
@@ -463,9 +462,15 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
                 GlobalAlloc::Memory(target_alloc) => {
                     data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
                 }
-                GlobalAlloc::VTable(ty, dyn_ty) => {
-                    data_id_for_vtable(tcx, cx, module, ty, dyn_ty.principal())
-                }
+                GlobalAlloc::VTable(ty, dyn_ty) => data_id_for_vtable(
+                    tcx,
+                    cx,
+                    module,
+                    ty,
+                    dyn_ty
+                        .principal()
+                        .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
+                ),
                 GlobalAlloc::Static(def_id) => {
                     if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
                     {
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index 2843e5bbdfb2..f8bbb2149201 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -61,7 +61,12 @@ pub(crate) fn unsized_info<'tcx>(
                 old_info
             }
         }
-        (_, ty::Dynamic(data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()),
+        (_, ty::Dynamic(data, ..)) => crate::vtable::get_vtable(
+            fx,
+            source,
+            data.principal()
+                .map(|principal| fx.tcx.instantiate_bound_regions_with_erased(principal)),
+        ),
         _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs
index 82b6178be9dc..a460023b59cb 100644
--- a/compiler/rustc_codegen_cranelift/src/vtable.rs
+++ b/compiler/rustc_codegen_cranelift/src/vtable.rs
@@ -90,7 +90,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
 pub(crate) fn get_vtable<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     ty: Ty<'tcx>,
-    trait_ref: Option>,
+    trait_ref: Option>,
 ) -> Value {
     let data_id = data_id_for_vtable(fx.tcx, &mut fx.constants_cx, fx.module, ty, trait_ref);
     let local_data_id = fx.module.declare_data_in_func(data_id, fx.bcx.func);
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 30732c74eb3e..570ef938dc44 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -14,7 +14,7 @@ use rustc_middle::ty::layout::{
     FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError,
     LayoutOfHelpers,
 };
-use rustc_middle::ty::{self, Instance, PolyExistentialTraitRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_span::source_map::respan;
 use rustc_span::{DUMMY_SP, Span};
@@ -90,7 +90,7 @@ pub struct CodegenCx<'gcc, 'tcx> {
     pub function_instances: RefCell, Function<'gcc>>>,
     /// Cache generated vtables
     pub vtables:
-        RefCell, Option>), RValue<'gcc>>>,
+        RefCell, Option>), RValue<'gcc>>>,
 
     // TODO(antoyo): improve the SSA API to not require those.
     /// Mapping from function pointer type to indexes of on stack parameters.
@@ -401,7 +401,7 @@ impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> {
 impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
     fn vtables(
         &self,
-    ) -> &RefCell, Option>), RValue<'gcc>>> {
+    ) -> &RefCell, Option>), RValue<'gcc>>> {
         &self.vtables
     }
 
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
index 4b84b1dbfd39..86d3de225f71 100644
--- a/compiler/rustc_codegen_gcc/src/debuginfo.rs
+++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs
@@ -7,7 +7,7 @@ use rustc_data_structures::sync::Lrc;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_index::{Idx, IndexVec};
 use rustc_middle::mir::{self, Body, SourceScope};
-use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
+use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty};
 use rustc_session::config::DebugInfo;
 use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol};
 use rustc_target::abi::Size;
@@ -214,7 +214,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
     fn create_vtable_debuginfo(
         &self,
         _ty: Ty<'tcx>,
-        _trait_ref: Option>,
+        _trait_ref: Option>,
         _vtable: Self::Value,
     ) {
         // TODO(antoyo)
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 79381f35a3cd..ba4fd75fb941 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -77,8 +77,7 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {
     /// Cache instances of monomorphic and polymorphic items
     pub instances: RefCell, &'ll Value>>,
     /// Cache generated vtables
-    pub vtables:
-        RefCell, Option>), &'ll Value>>,
+    pub vtables: RefCell, Option>), &'ll Value>>,
     /// Cache of constant strings,
     pub const_str_cache: RefCell>,
 
@@ -663,15 +662,14 @@ impl<'ll> SimpleCx<'ll> {
 impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn vtables(
         &self,
-    ) -> &RefCell, Option>), &'ll Value>>
-    {
+    ) -> &RefCell, Option>), &'ll Value>> {
         &self.vtables
     }
 
     fn apply_vcall_visibility_metadata(
         &self,
         ty: Ty<'tcx>,
-        poly_trait_ref: Option>,
+        poly_trait_ref: Option>,
         vtable: &'ll Value,
     ) {
         apply_vcall_visibility_metadata(self, ty, poly_trait_ref, vtable);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 587cb1bef2bd..82c7b863a9d0 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -13,7 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{
-    self, AdtKind, CoroutineArgsExt, Instance, PolyExistentialTraitRef, Ty, TyCtxt, Visibility,
+    self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility,
 };
 use rustc_session::config::{self, DebugInfo, Lto};
 use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene};
@@ -1400,13 +1400,13 @@ pub(crate) fn build_global_var_di_node<'ll>(
 fn build_vtable_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ty: Ty<'tcx>,
-    poly_trait_ref: Option>,
+    poly_trait_ref: Option>,
 ) -> &'ll DIType {
     let tcx = cx.tcx;
 
     let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
         let trait_ref = poly_trait_ref.with_self_ty(tcx, ty);
-        let trait_ref = tcx.erase_regions(tcx.instantiate_bound_regions_with_erased(trait_ref));
+        let trait_ref = tcx.erase_regions(trait_ref);
 
         tcx.vtable_entries(trait_ref)
     } else {
@@ -1491,7 +1491,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
 pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ty: Ty<'tcx>,
-    trait_ref: Option>,
+    trait_ref: Option>,
     vtable: &'ll Value,
 ) {
     // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in
@@ -1510,7 +1510,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
 
     let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty);
     let trait_ref_self = cx.tcx.erase_regions(trait_ref_self);
-    let trait_def_id = trait_ref_self.def_id();
+    let trait_def_id = trait_ref_self.def_id;
     let trait_vis = cx.tcx.visibility(trait_def_id);
 
     let cgus = cx.sess().codegen_units().as_usize();
@@ -1569,7 +1569,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
 pub(crate) fn create_vtable_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ty: Ty<'tcx>,
-    poly_trait_ref: Option>,
+    poly_trait_ref: Option>,
     vtable: &'ll Value,
 ) {
     if cx.dbg_cx.is_none() {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
index a37e719d43f2..af1d503ad6a4 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
@@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_macros::HashStable;
 use rustc_middle::bug;
-use rustc_middle::ty::{self, PolyExistentialTraitRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt};
 
 use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata};
 use crate::common::{AsCCharPtr, CodegenCx};
@@ -44,7 +44,7 @@ pub(super) enum UniqueTypeId<'tcx> {
     /// The ID for the additional wrapper struct type describing an enum variant in CPP-like mode.
     VariantStructTypeCppLikeWrapper(Ty<'tcx>, VariantIdx, private::HiddenZst),
     /// The ID of the artificial type we create for VTables.
-    VTableTy(Ty<'tcx>, Option>, private::HiddenZst),
+    VTableTy(Ty<'tcx>, Option>, private::HiddenZst),
 }
 
 impl<'tcx> UniqueTypeId<'tcx> {
@@ -88,7 +88,7 @@ impl<'tcx> UniqueTypeId<'tcx> {
     pub(crate) fn for_vtable_ty(
         tcx: TyCtxt<'tcx>,
         self_type: Ty<'tcx>,
-        implemented_trait: Option>,
+        implemented_trait: Option>,
     ) -> Self {
         assert_eq!(
             self_type,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index e6778411365f..c58570c02da2 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -585,7 +585,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn create_vtable_debuginfo(
         &self,
         ty: Ty<'tcx>,
-        trait_ref: Option>,
+        trait_ref: Option>,
         vtable: Self::Value,
     ) {
         metadata::create_vtable_di_node(self, ty, trait_ref, vtable)
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index e438bd70c510..f6874b66b1b5 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -25,7 +25,6 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
 use rustc_span::{DUMMY_SP, Symbol, sym};
-use rustc_trait_selection::infer::at::ToTrace;
 use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt};
 use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 use tracing::{debug, info};
@@ -129,14 +128,9 @@ pub fn validate_trivial_unsize<'tcx>(
                     BoundRegionConversionTime::HigherRankedType,
                     hr_source_principal,
                 );
-                let Ok(()) = ocx.eq_trace(
+                let Ok(()) = ocx.eq(
                     &ObligationCause::dummy(),
                     param_env,
-                    ToTrace::to_trace(
-                        &ObligationCause::dummy(),
-                        hr_target_principal,
-                        hr_source_principal,
-                    ),
                     target_principal,
                     source_principal,
                 ) else {
@@ -211,7 +205,12 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 old_info
             }
         }
-        (_, ty::Dynamic(data, _, _)) => meth::get_vtable(cx, source, data.principal()),
+        (_, ty::Dynamic(data, _, _)) => meth::get_vtable(
+            cx,
+            source,
+            data.principal()
+                .map(|principal| bx.tcx().instantiate_bound_regions_with_erased(principal)),
+        ),
         _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 869798d8be19..d1b1ccbb8127 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -507,7 +507,7 @@ pub enum VTableNameKind {
 pub fn compute_debuginfo_vtable_name<'tcx>(
     tcx: TyCtxt<'tcx>,
     t: Ty<'tcx>,
-    trait_ref: Option>,
+    trait_ref: Option>,
     kind: VTableNameKind,
 ) -> String {
     let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
@@ -530,8 +530,8 @@ pub fn compute_debuginfo_vtable_name<'tcx>(
     }
 
     if let Some(trait_ref) = trait_ref {
-        let trait_ref = tcx
-            .normalize_erasing_late_bound_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
+        let trait_ref =
+            tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
         push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
         visited.clear();
         push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs
index c51a57be71ce..886951855401 100644
--- a/compiler/rustc_codegen_ssa/src/meth.rs
+++ b/compiler/rustc_codegen_ssa/src/meth.rs
@@ -1,5 +1,5 @@
 use rustc_middle::bug;
-use rustc_middle::ty::{self, GenericArgKind, Ty};
+use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt};
 use rustc_session::config::Lto;
 use rustc_symbol_mangling::typeid_for_trait_ref;
 use rustc_target::callconv::FnAbi;
@@ -72,12 +72,17 @@ impl<'a, 'tcx> VirtualIndex {
 
 /// This takes a valid `self` receiver type and extracts the principal trait
 /// ref of the type. Return `None` if there is no principal trait.
-fn dyn_trait_in_self(ty: Ty<'_>) -> Option> {
+fn dyn_trait_in_self<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option> {
     for arg in ty.peel_refs().walk() {
         if let GenericArgKind::Type(ty) = arg.unpack()
             && let ty::Dynamic(data, _, _) = ty.kind()
         {
-            return data.principal();
+            return data
+                .principal()
+                .map(|principal| tcx.instantiate_bound_regions_with_erased(principal));
         }
     }
 
@@ -96,28 +101,24 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option> {
 pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
     cx: &Cx,
     ty: Ty<'tcx>,
-    poly_trait_ref: Option>,
+    trait_ref: Option>,
 ) -> Cx::Value {
     let tcx = cx.tcx();
 
     // Check the cache.
-    if let Some(&val) = cx.vtables().borrow().get(&(ty, poly_trait_ref)) {
+    if let Some(&val) = cx.vtables().borrow().get(&(ty, trait_ref)) {
         return val;
     }
 
-    // FIXME(trait_upcasting): Take a non-higher-ranked vtable as arg.
-    let trait_ref =
-        poly_trait_ref.map(|trait_ref| tcx.instantiate_bound_regions_with_erased(trait_ref));
-
     let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref));
     let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory();
     let vtable_const = cx.const_data_from_alloc(vtable_allocation);
     let align = cx.data_layout().pointer_align.abi;
     let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
 
-    cx.apply_vcall_visibility_metadata(ty, poly_trait_ref, vtable);
-    cx.create_vtable_debuginfo(ty, poly_trait_ref, vtable);
-    cx.vtables().borrow_mut().insert((ty, poly_trait_ref), vtable);
+    cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable);
+    cx.create_vtable_debuginfo(ty, trait_ref, vtable);
+    cx.vtables().borrow_mut().insert((ty, trait_ref), vtable);
     vtable
 }
 
@@ -135,7 +136,7 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     if bx.cx().sess().opts.unstable_opts.virtual_function_elimination
         && bx.cx().sess().lto() == Lto::Fat
     {
-        if let Some(trait_ref) = dyn_trait_in_self(ty) {
+        if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) {
             let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap();
             let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
             return func;
diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
index fe135e911fb3..30d77c206a56 100644
--- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
@@ -2,7 +2,7 @@ use std::ops::Range;
 
 use rustc_abi::Size;
 use rustc_middle::mir;
-use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
+use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty};
 use rustc_span::{SourceFile, Span, Symbol};
 use rustc_target::callconv::FnAbi;
 
@@ -13,7 +13,7 @@ pub trait DebugInfoCodegenMethods<'tcx>: BackendTypes {
     fn create_vtable_debuginfo(
         &self,
         ty: Ty<'tcx>,
-        trait_ref: Option>,
+        trait_ref: Option>,
         vtable: Self::Value,
     );
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs
index 5b33fd7ab102..4004947b4648 100644
--- a/compiler/rustc_codegen_ssa/src/traits/misc.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs
@@ -10,11 +10,11 @@ use super::BackendTypes;
 pub trait MiscCodegenMethods<'tcx>: BackendTypes {
     fn vtables(
         &self,
-    ) -> &RefCell, Option>), Self::Value>>;
+    ) -> &RefCell, Option>), Self::Value>>;
     fn apply_vcall_visibility_metadata(
         &self,
         _ty: Ty<'tcx>,
-        _poly_trait_ref: Option>,
+        _poly_trait_ref: Option>,
         _vtable: Self::Value,
     ) {
     }
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 5c5ab435dbd7..4312c82815c1 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -151,7 +151,7 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty
 
 pub fn typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyExistentialTraitRef<'tcx>,
+    trait_ref: ty::ExistentialTraitRef<'tcx>,
 ) -> String {
     v0::mangle_typeid_for_trait_ref(tcx, trait_ref)
 }
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 4ddf530a00d4..45d89fbf9209 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -72,7 +72,7 @@ pub(super) fn mangle<'tcx>(
 
 pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyExistentialTraitRef<'tcx>,
+    trait_ref: ty::ExistentialTraitRef<'tcx>,
 ) -> String {
     // FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`.
     let mut cx = SymbolMangler {
@@ -84,7 +84,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
         binders: vec![],
         out: String::new(),
     };
-    cx.print_def_path(trait_ref.def_id(), &[]).unwrap();
+    cx.print_def_path(trait_ref.def_id, &[]).unwrap();
     std::mem::take(&mut cx.out)
 }
 

From 10fc0b159ee6e5281bf38f65680082961dd7bec3 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Mon, 27 Jan 2025 04:30:00 +0100
Subject: [PATCH 286/342] introduce `ty::Value`

Co-authored-by: FedericoBruzzone 
---
 .../src/intrinsics/simd.rs                    |  7 +--
 compiler/rustc_codegen_llvm/src/intrinsic.rs  |  2 +-
 .../src/debuginfo/type_names.rs               | 16 +++---
 .../rustc_codegen_ssa/src/mir/constant.rs     |  2 +-
 .../src/check_consts/qualifs.rs               |  2 +-
 compiler/rustc_const_eval/src/lib.rs          |  9 +++-
 compiler/rustc_infer/src/infer/freshen.rs     |  2 +-
 compiler/rustc_infer/src/infer/mod.rs         |  2 +-
 compiler/rustc_middle/src/mir/consts.rs       | 22 ++++----
 compiler/rustc_middle/src/mir/pretty.rs       |  4 +-
 compiler/rustc_middle/src/query/keys.rs       |  2 +-
 compiler/rustc_middle/src/query/mod.rs        |  6 +--
 compiler/rustc_middle/src/ty/consts.rs        | 38 ++++----------
 .../rustc_middle/src/ty/consts/valtree.rs     | 51 ++++++++++++++++---
 compiler/rustc_middle/src/ty/context.rs       | 26 ++++++----
 compiler/rustc_middle/src/ty/flags.rs         |  2 +-
 compiler/rustc_middle/src/ty/mod.rs           |  2 +-
 compiler/rustc_middle/src/ty/print/pretty.rs  | 51 +++++++++----------
 .../rustc_middle/src/ty/structural_impls.rs   | 18 +++----
 compiler/rustc_middle/src/ty/walk.rs          |  2 +-
 .../src/thir/pattern/const_to_pat.rs          |  2 +-
 .../src/canonicalizer.rs                      |  2 +-
 .../rustc_next_trait_solver/src/solve/mod.rs  |  6 +--
 .../src/cfi/typeid/itanium_cxx_abi/encode.rs  | 22 ++++----
 compiler/rustc_smir/src/rustc_smir/context.rs |  5 +-
 .../rustc_smir/src/rustc_smir/convert/ty.rs   | 23 +++------
 compiler/rustc_symbol_mangling/src/legacy.rs  |  7 +--
 compiler/rustc_symbol_mangling/src/v0.rs      |  7 +--
 .../src/solve/fulfill.rs                      |  2 +-
 .../src/traits/const_evaluatable.rs           |  2 +-
 .../src/traits/fulfill.rs                     |  2 +-
 .../src/traits/select/mod.rs                  |  2 +-
 compiler/rustc_transmute/src/lib.rs           |  8 +--
 compiler/rustc_ty_utils/src/consts.rs         |  6 +--
 compiler/rustc_ty_utils/src/layout.rs         | 19 +++----
 compiler/rustc_type_ir/src/const_kind.rs      |  4 +-
 compiler/rustc_type_ir/src/fast_reject.rs     |  4 +-
 compiler/rustc_type_ir/src/inherent.rs        |  5 ++
 compiler/rustc_type_ir/src/interner.rs        |  3 +-
 compiler/rustc_type_ir/src/relate.rs          |  4 +-
 src/librustdoc/clean/utils.rs                 |  6 +--
 .../clippy_lints/src/large_const_arrays.rs    |  5 +-
 .../clippy_lints/src/large_stack_arrays.rs    |  5 +-
 src/tools/miri/src/intrinsics/simd.rs         |  2 +-
 44 files changed, 214 insertions(+), 205 deletions(-)

diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 6d71b8e8abab..d682efd19aaa 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -129,12 +129,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
                 return;
             }
 
-            let idx = generic_args[2]
-                .expect_const()
-                .try_to_valtree()
-                .expect("expected monomorphic const in codegen")
-                .0
-                .unwrap_branch();
+            let idx = generic_args[2].expect_const().to_value().valtree.unwrap_branch();
 
             assert_eq!(x.layout(), y.layout());
             let layout = x.layout();
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index eab4a9f30c9d..43d6ccfcb4a2 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1329,7 +1329,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     }
 
     if name == sym::simd_shuffle_generic {
-        let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
+        let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
         let n = idx.len() as u64;
 
         let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 869798d8be19..d766ae37e5b3 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -673,25 +673,23 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
         ty::ConstKind::Param(param) => {
             write!(output, "{}", param.name)
         }
-        ty::ConstKind::Value(ty, valtree) => {
-            match ty.kind() {
+        ty::ConstKind::Value(cv) => {
+            match cv.ty.kind() {
                 ty::Int(ity) => {
-                    // FIXME: directly extract the bits from a valtree instead of evaluating an
-                    // already evaluated `Const` in order to get the bits.
-                    let bits = ct
+                    let bits = cv
                         .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
                         .expect("expected monomorphic const in codegen");
                     let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
                     write!(output, "{val}")
                 }
                 ty::Uint(_) => {
-                    let val = ct
+                    let val = cv
                         .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
                         .expect("expected monomorphic const in codegen");
                     write!(output, "{val}")
                 }
                 ty::Bool => {
-                    let val = ct.try_to_bool().expect("expected monomorphic const in codegen");
+                    let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
                     write!(output, "{val}")
                 }
                 _ => {
@@ -703,9 +701,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
                     // avoiding collisions and will make the emitted type names shorter.
                     let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
                         let mut hasher = StableHasher::new();
-                        hcx.while_hashing_spans(false, |hcx| {
-                            (ty, valtree).hash_stable(hcx, &mut hasher)
-                        });
+                        hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
                         hasher.finish::()
                     });
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 7676e1e171aa..eafc551501c1 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -43,7 +43,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             mir::Const::Ty(_, c) => match c.kind() {
                 // A constant that came from a const generic but was then used as an argument to
                 // old-style simd_shuffle (passing as argument instead of as a generic param).
-                rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)),
+                rustc_type_ir::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)),
                 other => span_bug!(constant.span, "{other:#?}"),
             },
             // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
index e244b50a4b5d..5d368b600a02 100644
--- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs
@@ -345,7 +345,7 @@ where
         Const::Ty(_, ct)
             if matches!(
                 ct.kind(),
-                ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _)
+                ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
             ) =>
         {
             None
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index b5adf06b3001..ecf9745b7794 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -46,8 +46,13 @@ pub fn provide(providers: &mut Providers) {
     };
     providers.hooks.try_destructure_mir_constant_for_user_output =
         const_eval::try_destructure_mir_constant_for_user_output;
-    providers.valtree_to_const_val = |tcx, (ty, valtree)| {
-        const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree)
+    providers.valtree_to_const_val = |tcx, cv| {
+        const_eval::valtree_to_const_value(
+            tcx,
+            ty::TypingEnv::fully_monomorphized(),
+            cv.ty,
+            cv.valtree,
+        )
     };
     providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
         util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 28eac5b7496d..74c8b463fc89 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -170,7 +170,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> {
             }
 
             ty::ConstKind::Param(_)
-            | ty::ConstKind::Value(_, _)
+            | ty::ConstKind::Value(_)
             | ty::ConstKind::Unevaluated(..)
             | ty::ConstKind::Expr(..)
             | ty::ConstKind::Error(_) => ct.super_fold_with(self),
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 283ebdfa236b..515c9c340985 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1055,7 +1055,7 @@ impl<'tcx> InferCtxt<'tcx> {
             | ty::ConstKind::Bound(_, _)
             | ty::ConstKind::Placeholder(_)
             | ty::ConstKind::Unevaluated(_)
-            | ty::ConstKind::Value(_, _)
+            | ty::ConstKind::Value(_)
             | ty::ConstKind::Error(_)
             | ty::ConstKind::Expr(_) => ct,
         }
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 66d97fda4333..923160cc0cc8 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -250,7 +250,7 @@ impl<'tcx> Const<'tcx> {
                     // Dont use the outer ty as on invalid code we can wind up with them not being the same.
                     // this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir
                     // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
-                    ty::ConstKind::Value(ty, _) => ty,
+                    ty::ConstKind::Value(cv) => cv.ty,
                     _ => *ty,
                 }
             }
@@ -264,7 +264,7 @@ impl<'tcx> Const<'tcx> {
     pub fn is_required_const(&self) -> bool {
         match self {
             Const::Ty(_, c) => match c.kind() {
-                ty::ConstKind::Value(_, _) => false, // already a value, cannot error
+                ty::ConstKind::Value(_) => false, // already a value, cannot error
                 _ => true,
             },
             Const::Val(..) => false, // already a value, cannot error
@@ -276,11 +276,11 @@ impl<'tcx> Const<'tcx> {
     pub fn try_to_scalar(self) -> Option {
         match self {
             Const::Ty(_, c) => match c.kind() {
-                ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
+                ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
                     // A valtree of a type where leaves directly represent the scalar const value.
                     // Just checking whether it is a leaf is insufficient as e.g. references are leafs
                     // but the leaf value is the value they point to, not the reference itself!
-                    Some(valtree.unwrap_leaf().into())
+                    Some(cv.valtree.unwrap_leaf().into())
                 }
                 _ => None,
             },
@@ -295,9 +295,7 @@ impl<'tcx> Const<'tcx> {
         match self {
             Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
             Const::Ty(_, c) => match c.kind() {
-                ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
-                    Some(valtree.unwrap_leaf())
-                }
+                ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
                 _ => None,
             },
             _ => None,
@@ -328,7 +326,7 @@ impl<'tcx> Const<'tcx> {
                 }
 
                 match c.kind() {
-                    ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))),
+                    ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
                     ConstKind::Expr(_) => {
                         bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
                     }
@@ -353,13 +351,13 @@ impl<'tcx> Const<'tcx> {
         typing_env: ty::TypingEnv<'tcx>,
     ) -> Option {
         if let Const::Ty(_, c) = self
-            && let ty::ConstKind::Value(ty, val) = c.kind()
-            && ty.is_primitive()
+            && let ty::ConstKind::Value(cv) = c.kind()
+            && cv.ty.is_primitive()
         {
             // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
             // are valtree leaves, and *not* on references. (References should return the
             // pointer here, which valtrees don't represent.)
-            Some(val.unwrap_leaf().into())
+            Some(cv.valtree.unwrap_leaf().into())
         } else {
             self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
         }
@@ -473,7 +471,7 @@ impl<'tcx> Const<'tcx> {
                 // A valtree may be a reference. Valtree references correspond to a
                 // different allocation each time they are evaluated. Valtrees for primitive
                 // types are fine though.
-                ty::ConstKind::Value(ty, _) => ty.is_primitive(),
+                ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
                 ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
                 // This can happen if evaluation of a constant failed. The result does not matter
                 // much since compilation is doomed.
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 3b4fba97e60b..a318bacb866d 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1441,7 +1441,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
                     ty::ConstKind::Unevaluated(uv) => {
                         format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
                     }
-                    ty::ConstKind::Value(_, val) => format!("ty::Valtree({})", fmt_valtree(&val)),
+                    ty::ConstKind::Value(cv) => {
+                        format!("ty::Valtree({})", fmt_valtree(&cv.valtree))
+                    }
                     // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
                     ty::ConstKind::Error(_) => "Error".to_string(),
                     // These variants shouldn't exist in the MIR.
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index e243425c0b7b..f7090fa69e2e 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -550,7 +550,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) {
     }
 }
 
-impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
+impl<'tcx> Key for ty::Value<'tcx> {
     type Cache = DefaultCache;
 
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d83bc19a6a2f..560e3bf3adeb 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1256,9 +1256,9 @@ rustc_queries! {
         desc { "evaluating type-level constant" }
     }
 
-    /// Converts a type level constant value into `ConstValue`
-    query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> {
-        desc { "converting type-level constant value to mir constant value"}
+    /// Converts a type-level constant value into a MIR constant value.
+    query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
+        desc { "converting type-level constant value to MIR constant value"}
     }
 
     /// Destructures array, ADT or tuple constants into the constants
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 310552764224..6875532e28aa 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -110,8 +110,8 @@ impl<'tcx> Const<'tcx> {
     }
 
     #[inline]
-    pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
-        Const::new(tcx, ty::ConstKind::Value(ty, val))
+    pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+        Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree }))
     }
 
     #[inline]
@@ -214,47 +214,31 @@ impl<'tcx> Const<'tcx> {
         Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize)
     }
 
-    /// Panics if self.kind != ty::ConstKind::Value
-    pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
+    /// Panics if `self.kind != ty::ConstKind::Value`.
+    pub fn to_value(self) -> ty::Value<'tcx> {
         match self.kind() {
-            ty::ConstKind::Value(ty, valtree) => (valtree, ty),
+            ty::ConstKind::Value(cv) => cv,
             _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
         }
     }
 
-    /// Attempts to convert to a `ValTree`
-    pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> {
+    /// Attempts to convert to a value.
+    pub fn try_to_value(self) -> Option> {
         match self.kind() {
-            ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)),
+            ty::ConstKind::Value(cv) => Some(cv),
             _ => None,
         }
     }
 
     #[inline]
     pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> {
-        let (valtree, ty) = self.try_to_valtree()?;
-        Some((valtree.try_to_scalar()?, ty))
-    }
-
-    pub fn try_to_bool(self) -> Option {
-        self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok()
+        let cv = self.try_to_value()?;
+        Some((cv.valtree.try_to_scalar()?, cv.ty))
     }
 
     #[inline]
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option {
-        self.try_to_valtree()?.0.try_to_target_usize(tcx)
-    }
-
-    /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
-    /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
-    /// contains const generic parameters or pointers).
-    #[inline]
-    pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option {
-        let (scalar, ty) = self.try_to_scalar()?;
-        let scalar = scalar.try_to_scalar_int().ok()?;
-        let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(ty);
-        let size = tcx.layout_of(input).ok()?.size;
-        Some(scalar.to_bits(size))
+        self.try_to_value()?.try_to_target_usize(tcx)
     }
 
     pub fn is_ct_infer(self) -> bool {
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index 9f9bf41c3355..425b68a951c6 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -1,11 +1,9 @@
-use rustc_macros::{HashStable, TyDecodable, TyEncodable};
+use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 
 use super::ScalarInt;
 use crate::mir::interpret::Scalar;
 use crate::ty::{self, Ty, TyCtxt};
 
-#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)]
-#[derive(HashStable)]
 /// This datastructure is used to represent the value of constants used in the type system.
 ///
 /// We explicitly choose a different datastructure from the way values are processed within
@@ -18,6 +16,8 @@ use crate::ty::{self, Ty, TyCtxt};
 ///
 /// `ValTree` does not have this problem with representation, as it only contains integers or
 /// lists of (nested) `ValTree`.
+#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(HashStable, TyEncodable, TyDecodable)]
 pub enum ValTree<'tcx> {
     /// integers, `bool`, `char` are represented as scalars.
     /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
@@ -79,10 +79,6 @@ impl<'tcx> ValTree<'tcx> {
         }
     }
 
-    pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option {
-        self.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
-    }
-
     /// Get the values inside the ValTree as a slice of bytes. This only works for
     /// constants with types &str, &[u8], or [u8; _].
     pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> {
@@ -107,3 +103,44 @@ impl<'tcx> ValTree<'tcx> {
         )
     }
 }
+
+/// A type-level constant value.
+///
+/// Represents a typed, fully evaluated constant.
+#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
+#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
+pub struct Value<'tcx> {
+    pub ty: Ty<'tcx>,
+    pub valtree: ValTree<'tcx>,
+}
+
+impl<'tcx> Value<'tcx> {
+    /// Attempts to extract the raw bits from the constant.
+    ///
+    /// Fails if the value can't be represented as bits (e.g. because it is an aggregate).
+    #[inline]
+    pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option {
+        let scalar = self.valtree.try_to_scalar_int()?;
+        let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty);
+        let size = tcx.layout_of(input).ok()?.size;
+        Some(scalar.to_bits(size))
+    }
+
+    pub fn try_to_bool(self) -> Option {
+        self.valtree.try_to_scalar_int()?.try_to_bool().ok()
+    }
+
+    pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option {
+        self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
+    }
+}
+
+impl<'tcx> rustc_type_ir::inherent::ValueConst> for Value<'tcx> {
+    fn ty(self) -> Ty<'tcx> {
+        self.ty
+    }
+
+    fn valtree(self) -> ValTree<'tcx> {
+        self.valtree
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 0c22c056dab5..69f6fc0ad8a6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -142,10 +142,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type ParamConst = ty::ParamConst;
     type BoundConst = ty::BoundVar;
-    type ValueConst = ty::ValTree<'tcx>;
+    type ValueConst = ty::Value<'tcx>;
     type ExprConst = ty::Expr<'tcx>;
-    type Region = Region<'tcx>;
+    type ValTree = ty::ValTree<'tcx>;
 
+    type Region = Region<'tcx>;
     type EarlyParamRegion = ty::EarlyParamRegion;
     type LateParamRegion = ty::LateParamRegion;
     type BoundRegion = ty::BoundRegion;
@@ -1118,15 +1119,18 @@ impl<'tcx> CommonConsts<'tcx> {
         };
 
         CommonConsts {
-            unit: mk_const(ty::ConstKind::Value(types.unit, ty::ValTree::zst())),
-            true_: mk_const(ty::ConstKind::Value(
-                types.bool,
-                ty::ValTree::Leaf(ty::ScalarInt::TRUE),
-            )),
-            false_: mk_const(ty::ConstKind::Value(
-                types.bool,
-                ty::ValTree::Leaf(ty::ScalarInt::FALSE),
-            )),
+            unit: mk_const(ty::ConstKind::Value(ty::Value {
+                ty: types.unit,
+                valtree: ty::ValTree::zst(),
+            })),
+            true_: mk_const(ty::ConstKind::Value(ty::Value {
+                ty: types.bool,
+                valtree: ty::ValTree::Leaf(ty::ScalarInt::TRUE),
+            })),
+            false_: mk_const(ty::ConstKind::Value(ty::Value {
+                ty: types.bool,
+                valtree: ty::ValTree::Leaf(ty::ScalarInt::FALSE),
+            })),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 0af57f636aaa..ec0498b168c0 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -381,7 +381,7 @@ impl FlagComputation {
                 self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER);
                 self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
             }
-            ty::ConstKind::Value(ty, _) => self.add_ty(ty),
+            ty::ConstKind::Value(cv) => self.add_ty(cv.ty),
             ty::ConstKind::Expr(e) => self.add_args(e.args()),
             ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
         }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 8cd632790a8a..88eea6101b5f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -60,7 +60,7 @@ pub use self::closure::{
     place_to_string_for_capture,
 };
 pub use self::consts::{
-    Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree,
+    Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, Value,
 };
 pub use self::context::{
     CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 027a4315b4bf..19a39b3a29ea 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1484,8 +1484,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 _ => write!(self, "_")?,
             },
             ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
-            ty::ConstKind::Value(ty, value) => {
-                return self.pretty_print_const_valtree(value, ty, print_ty);
+            ty::ConstKind::Value(cv) => {
+                return self.pretty_print_const_valtree(cv.valtree, cv.ty, print_ty);
             }
 
             ty::ConstKind::Bound(debruijn, bound_var) => {
@@ -1637,33 +1637,32 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         match ty.kind() {
             // Byte strings (&[u8; N])
             ty::Ref(_, inner, _) => {
-                if let ty::Array(elem, len) = inner.kind() {
-                    if let ty::Uint(ty::UintTy::U8) = elem.kind() {
-                        if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.kind() {
-                            match self.tcx().try_get_global_alloc(prov.alloc_id()) {
-                                Some(GlobalAlloc::Memory(alloc)) => {
-                                    let len = int.to_bits(self.tcx().data_layout.pointer_size);
-                                    let range =
-                                        AllocRange { start: offset, size: Size::from_bytes(len) };
-                                    if let Ok(byte_str) =
-                                        alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
-                                    {
-                                        p!(pretty_print_byte_str(byte_str))
-                                    } else {
-                                        p!("")
-                                    }
-                                }
-                                // FIXME: for statics, vtables, and functions, we could in principle print more detail.
-                                Some(GlobalAlloc::Static(def_id)) => {
-                                    p!(write("", def_id))
-                                }
-                                Some(GlobalAlloc::Function { .. }) => p!(""),
-                                Some(GlobalAlloc::VTable(..)) => p!(""),
-                                None => p!(""),
+                if let ty::Array(elem, len) = inner.kind()
+                    && let ty::Uint(ty::UintTy::U8) = elem.kind()
+                    && let ty::ConstKind::Value(cv) = len.kind()
+                    && let ty::ValTree::Leaf(int) = cv.valtree
+                {
+                    match self.tcx().try_get_global_alloc(prov.alloc_id()) {
+                        Some(GlobalAlloc::Memory(alloc)) => {
+                            let len = int.to_bits(self.tcx().data_layout.pointer_size);
+                            let range = AllocRange { start: offset, size: Size::from_bytes(len) };
+                            if let Ok(byte_str) =
+                                alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
+                            {
+                                p!(pretty_print_byte_str(byte_str))
+                            } else {
+                                p!("")
                             }
-                            return Ok(());
                         }
+                        // FIXME: for statics, vtables, and functions, we could in principle print more detail.
+                        Some(GlobalAlloc::Static(def_id)) => {
+                            p!(write("", def_id))
+                        }
+                        Some(GlobalAlloc::Function { .. }) => p!(""),
+                        Some(GlobalAlloc::VTable(..)) => p!(""),
+                        None => p!(""),
                     }
+                    return Ok(());
                 }
             }
             ty::FnPtr(..) => {
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 68cb56f35837..9e9de4fb0644 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -162,16 +162,15 @@ impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> {
 impl<'tcx> fmt::Debug for ty::Const<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // If this is a value, we spend some effort to make it look nice.
-        if let ConstKind::Value(_, _) = self.kind() {
+        if let ConstKind::Value(_) = self.kind() {
             return ty::tls::with(move |tcx| {
-                // Somehow trying to lift the valtree results in lifetime errors, so we lift the
-                // entire constant.
+                // ValTrees aren't interned, so we lift the entire constant.
                 let lifted = tcx.lift(*self).unwrap();
-                let ConstKind::Value(ty, valtree) = lifted.kind() else {
+                let ConstKind::Value(cv) = lifted.kind() else {
                     bug!("we checked that this is a valtree")
                 };
                 let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
-                cx.pretty_print_const_valtree(valtree, ty, /*print_ty*/ true)?;
+                cx.pretty_print_const_valtree(cv.valtree, cv.ty, /*print_ty*/ true)?;
                 f.write_str(&cx.into_buffer())
             });
         }
@@ -589,9 +588,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> {
             }
             ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?),
             ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
-            ConstKind::Value(t, v) => {
-                ConstKind::Value(t.try_fold_with(folder)?, v.try_fold_with(folder)?)
-            }
+            ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
             ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?),
             ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
         };
@@ -610,10 +607,7 @@ impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> {
             }
             ConstKind::Placeholder(p) => p.visit_with(visitor),
             ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
-            ConstKind::Value(t, v) => {
-                try_visit!(t.visit_with(visitor));
-                v.visit_with(visitor)
-            }
+            ConstKind::Value(v) => v.visit_with(visitor),
             ConstKind::Error(e) => e.visit_with(visitor),
             ConstKind::Expr(e) => e.visit_with(visitor),
         }
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index 2dcba8c2f82c..3e8a3d1a2892 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -206,7 +206,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
             | ty::ConstKind::Bound(..)
             | ty::ConstKind::Error(_) => {}
 
-            ty::ConstKind::Value(ty, _) => stack.push(ty.into()),
+            ty::ConstKind::Value(cv) => stack.push(cv.ty.into()),
 
             ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()),
             ty::ConstKind::Unevaluated(ct) => {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 3853b95f78b4..c490e3885d4d 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -46,7 +46,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
         match c.kind() {
             ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
-            ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty),
+            ty::ConstKind::Value(cv) => convert.valtree_to_pat(cv.valtree, cv.ty),
             _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
         }
     }
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 62a7c84bc280..7eeed721d5a0 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -522,7 +522,7 @@ impl, I: Interner> TypeFolder for Canonicaliz
             // FIXME: See comment above -- we could fold the region separately or something.
             ty::ConstKind::Bound(_, _)
             | ty::ConstKind::Unevaluated(_)
-            | ty::ConstKind::Value(_, _)
+            | ty::ConstKind::Value(_)
             | ty::ConstKind::Error(_)
             | ty::ConstKind::Expr(_) => return c.super_fold_with(self),
         };
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 8d1194ee5398..1fa35b60304c 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -160,9 +160,7 @@ where
             ty::ConstKind::Infer(_) => {
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
             }
-            ty::ConstKind::Placeholder(_)
-            | ty::ConstKind::Value(_, _)
-            | ty::ConstKind::Error(_) => {
+            ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
             // We can freely ICE here as:
@@ -199,7 +197,7 @@ where
                 unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
             }
             ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
-            ty::ConstKind::Value(ty, _) => ty,
+            ty::ConstKind::Value(cv) => cv.ty(),
             ty::ConstKind::Placeholder(placeholder) => {
                 self.cx().find_const_ty_from_env(goal.param_env, placeholder)
             }
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index 09648e28df47..5f0c1afdf642 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -103,7 +103,7 @@ fn encode_args<'tcx>(
 /// ).
 fn encode_const<'tcx>(
     tcx: TyCtxt<'tcx>,
-    c: Const<'tcx>,
+    ct: Const<'tcx>,
     ct_ty: Ty<'tcx>,
     dict: &mut FxHashMap, usize>,
     options: EncodeTyOptions,
@@ -111,7 +111,7 @@ fn encode_const<'tcx>(
     // L[n][]E as literal argument
     let mut s = String::from('L');
 
-    match c.kind() {
+    match ct.kind() {
         // Const parameters
         ty::ConstKind::Param(..) => {
             // LE as literal argument
@@ -121,18 +121,18 @@ fn encode_const<'tcx>(
         }
 
         // Literal arguments
-        ty::ConstKind::Value(ct_ty, ..) => {
+        ty::ConstKind::Value(cv) => {
             // L[n]E as literal argument
 
             // Element type
-            s.push_str(&encode_ty(tcx, ct_ty, dict, options));
+            s.push_str(&encode_ty(tcx, cv.ty, dict, options));
 
             // The only allowed types of const values are bool, u8, u16, u32,
             // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
             // bool value false is encoded as 0 and true as 1.
-            match ct_ty.kind() {
+            match cv.ty.kind() {
                 ty::Int(ity) => {
-                    let bits = c
+                    let bits = cv
                         .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
                         .expect("expected monomorphic const in cfi");
                     let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
@@ -142,30 +142,30 @@ fn encode_const<'tcx>(
                     let _ = write!(s, "{val}");
                 }
                 ty::Uint(_) => {
-                    let val = c
+                    let val = cv
                         .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
                         .expect("expected monomorphic const in cfi");
                     let _ = write!(s, "{val}");
                 }
                 ty::Bool => {
-                    let val = c.try_to_bool().expect("expected monomorphic const in cfi");
+                    let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
                     let _ = write!(s, "{val}");
                 }
                 _ => {
-                    bug!("encode_const: unexpected type `{:?}`", ct_ty);
+                    bug!("encode_const: unexpected type `{:?}`", cv.ty);
                 }
             }
         }
 
         _ => {
-            bug!("encode_const: unexpected kind `{:?}`", c.kind());
+            bug!("encode_const: unexpected kind `{:?}`", ct.kind());
         }
     }
 
     // Close the "L..E" pair
     s.push('E');
 
-    compress(dict, DictKey::Const(c), &mut s);
+    compress(dict, DictKey::Const(ct), &mut s);
 
     s
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 31c7e6c3eb44..8d4e55dc036d 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -450,8 +450,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let tcx = tables.tcx;
         let ty = ty::Ty::new_static_str(tcx);
         let bytes = value.as_bytes();
-        let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes);
-        let val = tcx.valtree_to_const_val((ty, val_tree));
+        let valtree = ty::ValTree::from_raw_bytes(tcx, bytes);
+        let cv = ty::Value { ty, valtree };
+        let val = tcx.valtree_to_const_val(cv);
         mir::Const::from_value(val, ty).stable(&mut tables)
     }
 
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index ff452eea23d2..a2d95f0f693a 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -418,23 +418,16 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
     type T = stable_mir::ty::TyConst;
 
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        let kind = match self.kind() {
-            ty::ConstKind::Value(ty, val) => {
-                let val = match val {
-                    ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar),
-                    ty::ValTree::Branch(branch) => {
-                        ty::ValTree::Branch(tables.tcx.lift(branch).unwrap())
-                    }
-                };
-
-                let ty = tables.tcx.lift(ty).unwrap();
-                let const_val = tables.tcx.valtree_to_const_val((ty, val));
+        let ct = tables.tcx.lift(*self).unwrap();
+        let kind = match ct.kind() {
+            ty::ConstKind::Value(cv) => {
+                let const_val = tables.tcx.valtree_to_const_val(cv);
                 if matches!(const_val, mir::ConstValue::ZeroSized) {
-                    stable_mir::ty::TyConstKind::ZSTValue(ty.stable(tables))
+                    stable_mir::ty::TyConstKind::ZSTValue(cv.ty.stable(tables))
                 } else {
                     stable_mir::ty::TyConstKind::Value(
-                        ty.stable(tables),
-                        alloc::new_allocation(ty, const_val, tables),
+                        cv.ty.stable(tables),
+                        alloc::new_allocation(cv.ty, const_val, tables),
                     )
                 }
             }
@@ -449,7 +442,7 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
             ty::ConstKind::Placeholder(_) => unimplemented!(),
             ty::ConstKind::Expr(_) => unimplemented!(),
         };
-        let id = tables.intern_ty_const(tables.tcx.lift(*self).unwrap());
+        let id = tables.intern_ty_const(ct);
         stable_mir::ty::TyConst::new(kind, id)
     }
 }
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 879f3fac21fb..8ae35572d012 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -274,14 +274,15 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
     fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
         // only print integers
         match ct.kind() {
-            ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) if ty.is_integral() => {
+            ty::ConstKind::Value(cv) if cv.ty.is_integral() => {
                 // The `pretty_print_const` formatting depends on -Zverbose-internals
                 // flag, so we cannot reuse it here.
-                let signed = matches!(ty.kind(), ty::Int(_));
+                let scalar = cv.valtree.unwrap_leaf();
+                let signed = matches!(cv.ty.kind(), ty::Int(_));
                 write!(
                     self,
                     "{:#?}",
-                    ty::ConstInt::new(scalar, signed, ty.is_ptr_sized_integral())
+                    ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral())
                 )?;
             }
             _ => self.write_str("_")?,
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 4ddf530a00d4..5b3272feecdb 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -590,8 +590,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
 
     fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
         // We only mangle a typed value if the const can be evaluated.
-        let (ct_ty, valtree) = match ct.kind() {
-            ty::ConstKind::Value(ty, val) => (ty, val),
+        let cv = match ct.kind() {
+            ty::ConstKind::Value(cv) => cv,
 
             // Should only be encountered within the identity-substituted
             // impl header of an item nested within an impl item.
@@ -619,13 +619,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             return Ok(());
         }
 
+        let ty::Value { ty: ct_ty, valtree } = cv;
         let start = self.out.len();
 
         match ct_ty.kind() {
             ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
                 ct_ty.print(self)?;
 
-                let mut bits = ct
+                let mut bits = cv
                     .try_to_bits(self.tcx, ty::TypingEnv::fully_monomorphized())
                     .expect("expected const to be monomorphic");
 
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 2b7da4bc5ff0..c8ae977b5ad6 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -264,7 +264,7 @@ fn fulfillment_error_for_no_solution<'tcx>(
                     infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
                 }
                 ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
-                ty::ConstKind::Value(ty, _) => ty,
+                ty::ConstKind::Value(cv) => cv.ty,
                 kind => span_bug!(
                     obligation.cause.span,
                     "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 446f9eaa348c..bdee6ca2afe5 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>(
         ty::ConstKind::Param(_)
         | ty::ConstKind::Bound(_, _)
         | ty::ConstKind::Placeholder(_)
-        | ty::ConstKind::Value(_, _)
+        | ty::ConstKind::Value(_)
         | ty::ConstKind::Error(_) => return Ok(()),
         ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
     };
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 7529ee128f5e..f2d2dd2f3ce5 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -479,7 +479,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         ty::ConstKind::Error(_) => {
                             return ProcessResult::Changed(PendingPredicateObligations::new());
                         }
-                        ty::ConstKind::Value(ty, _) => ty,
+                        ty::ConstKind::Value(cv) => cv.ty,
                         ty::ConstKind::Unevaluated(uv) => {
                             infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 6b6e0b32385c..99ce1fd9fb48 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -962,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             return Ok(EvaluatedToAmbig);
                         }
                         ty::ConstKind::Error(_) => return Ok(EvaluatedToOk),
-                        ty::ConstKind::Value(ty, _) => ty,
+                        ty::ConstKind::Value(cv) => cv.ty,
                         ty::ConstKind::Unevaluated(uv) => {
                             self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args)
                         }
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index ad86813c87e2..a50cc8f59325 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -128,16 +128,16 @@ mod rustc {
         pub fn from_const<'tcx>(
             tcx: TyCtxt<'tcx>,
             param_env: ParamEnv<'tcx>,
-            c: Const<'tcx>,
+            ct: Const<'tcx>,
         ) -> Option {
             use rustc_middle::ty::ScalarInt;
             use rustc_span::sym;
 
-            let Some((cv, ty)) = c.try_to_valtree() else {
+            let Some(cv) = ct.try_to_value() else {
                 return None;
             };
 
-            let adt_def = ty.ty_adt_def()?;
+            let adt_def = cv.ty.ty_adt_def()?;
 
             assert_eq!(
                 tcx.require_lang_item(LangItem::TransmuteOpts, None),
@@ -147,7 +147,7 @@ mod rustc {
             );
 
             let variant = adt_def.non_enum_variant();
-            let fields = match cv {
+            let fields = match cv.valtree {
                 ValTree::Branch(branch) => branch,
                 _ => {
                     return Some(Self {
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 51a7c976f600..931c36137ee5 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -22,16 +22,16 @@ fn destructure_const<'tcx>(
     tcx: TyCtxt<'tcx>,
     const_: ty::Const<'tcx>,
 ) -> ty::DestructuredConst<'tcx> {
-    let ty::ConstKind::Value(ct_ty, valtree) = const_.kind() else {
+    let ty::ConstKind::Value(cv) = const_.kind() else {
         bug!("cannot destructure constant {:?}", const_)
     };
 
-    let branches = match valtree {
+    let branches = match cv.valtree {
         ty::ValTree::Branch(b) => b,
         _ => bug!("cannot destructure constant {:?}", const_),
     };
 
-    let (fields, variant) = match ct_ty.kind() {
+    let (fields, variant) = match cv.ty.kind() {
         ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
             // construct the consts for the elements of the array/slice
             let field_consts = branches
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index a04c75361183..2f258b23f2df 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -144,13 +144,13 @@ fn univariant_uninterned<'tcx>(
     cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err))
 }
 
-fn validate_const_with_value<'tcx>(
+fn extract_const_value<'tcx>(
     const_: ty::Const<'tcx>,
     ty: Ty<'tcx>,
     cx: &LayoutCx<'tcx>,
-) -> Result, &'tcx LayoutError<'tcx>> {
+) -> Result, &'tcx LayoutError<'tcx>> {
     match const_.kind() {
-        ty::ConstKind::Value(..) => Ok(const_),
+        ty::ConstKind::Value(cv) => Ok(cv),
         ty::ConstKind::Error(guar) => {
             return Err(error(cx, LayoutError::ReferencesError(guar)));
         }
@@ -209,13 +209,12 @@ fn layout_of_uncached<'tcx>(
                         &mut layout.backend_repr
                     {
                         if let Some(start) = start {
-                            scalar.valid_range_mut().start =
-                                validate_const_with_value(start, ty, cx)?
-                                    .try_to_bits(tcx, cx.typing_env)
-                                    .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
+                            scalar.valid_range_mut().start = extract_const_value(start, ty, cx)?
+                                .try_to_bits(tcx, cx.typing_env)
+                                .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
                         }
                         if let Some(end) = end {
-                            let mut end = validate_const_with_value(end, ty, cx)?
+                            let mut end = extract_const_value(end, ty, cx)?
                                 .try_to_bits(tcx, cx.typing_env)
                                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
                             if !include_end {
@@ -348,9 +347,7 @@ fn layout_of_uncached<'tcx>(
 
         // Arrays and slices.
         ty::Array(element, count) => {
-            let count = validate_const_with_value(count, ty, cx)?
-                .to_valtree()
-                .0
+            let count = extract_const_value(count, ty, cx)?
                 .try_to_target_usize(tcx)
                 .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
 
diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs
index 03dfe547cedd..aafc0907ae15 100644
--- a/compiler/rustc_type_ir/src/const_kind.rs
+++ b/compiler/rustc_type_ir/src/const_kind.rs
@@ -31,7 +31,7 @@ pub enum ConstKind {
     Unevaluated(ty::UnevaluatedConst),
 
     /// Used to hold computed value.
-    Value(I::Ty, I::ValueConst),
+    Value(I::ValueConst),
 
     /// A placeholder for a const which could not be computed; this is
     /// propagated to avoid useless error messages.
@@ -52,7 +52,7 @@ impl fmt::Debug for ConstKind {
             Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var),
             Placeholder(placeholder) => write!(f, "{placeholder:?}"),
             Unevaluated(uv) => write!(f, "{uv:?}"),
-            Value(ty, valtree) => write!(f, "({valtree:?}: {ty:?})"),
+            Value(val) => write!(f, "{val:?}"),
             Error(_) => write!(f, "{{const error}}"),
             Expr(expr) => write!(f, "{expr:?}"),
         }
diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs
index 9b3ff14d507a..9955e92b55a5 100644
--- a/compiler/rustc_type_ir/src/fast_reject.rs
+++ b/compiler/rustc_type_ir/src/fast_reject.rs
@@ -483,8 +483,8 @@ impl match rhs.kind() {
-                ty::ConstKind::Value(_, rhs_val) => lhs_val == rhs_val,
+            ty::ConstKind::Value(lhs_val) => match rhs.kind() {
+                ty::ConstKind::Value(rhs_val) => lhs_val.valtree() == rhs_val.valtree(),
                 _ => false,
             },
 
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 872cf6680185..4e6d645e6fae 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -286,6 +286,11 @@ pub trait Const>:
     }
 }
 
+pub trait ValueConst>: Copy + Debug + Hash + Eq {
+    fn ty(self) -> I::Ty;
+    fn valtree(self) -> I::ValTree;
+}
+
 pub trait ExprConst>: Copy + Debug + Hash + Eq + Relate {
     fn args(self) -> I::GenericArgs;
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 0c3b0758f0f9..a6c72da0c564 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -112,8 +112,9 @@ pub trait Interner:
     type PlaceholderConst: PlaceholderLike;
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike;
-    type ValueConst: Copy + Debug + Hash + Eq;
+    type ValueConst: ValueConst;
     type ExprConst: ExprConst;
+    type ValTree: Copy + Debug + Hash + Eq;
 
     // Kinds of regions
     type Region: Region;
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index e628b66d2f08..5b696ee5ed40 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -606,7 +606,9 @@ pub fn structurally_relate_consts>(
             true
         }
         (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
-        (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
+        (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => {
+            a_val.valtree() == b_val.valtree()
+        }
 
         // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
         // and is the better alternative to waiting until `generic_const_exprs` can
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 77040aeb94d7..0b4fd9c22589 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -344,10 +344,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
             s
         }
         // array lengths are obviously usize
-        ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar))
-            if *ty.kind() == ty::Uint(ty::UintTy::Usize) =>
-        {
-            scalar.to_string()
+        ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => {
+            cv.valtree.unwrap_leaf().to_string()
         }
         _ => n.to_string(),
     }
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index 623b6b4fcc14..cabf10b7e0ea 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -56,9 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
             && !item.span.from_expansion()
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
             && let ty::Array(element_type, cst) = ty.kind()
-            && let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx
-                .try_normalize_erasing_regions(cx.typing_env(), *cst).unwrap_or(*cst).try_to_valtree()
-            && let element_count = element_count.to_target_usize(cx.tcx)
+            && let Some(element_count) = cx.tcx
+                .try_normalize_erasing_regions(cx.typing_env(), *cst).unwrap_or(*cst).try_to_target_usize(cx.tcx)
             && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
             && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)
         {
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
index 46d7df6995a9..6f5c5d6b3ea3 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -8,7 +8,7 @@ use clippy_utils::source::snippet;
 use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::ty::{self, ConstKind};
+use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::{Span, sym};
 
@@ -81,8 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
             && let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
             && !self.is_from_vec_macro(cx, expr.span)
             && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
-            && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind()
-            && let element_count = element_count.to_target_usize(cx.tcx)
+            && let Some(element_count) = cst.try_to_target_usize(cx.tcx)
             && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
             && !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
                 matches!(
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index 54bdd3f02c2c..63a61dcd1487 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -640,7 +640,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let (dest, dest_len) = this.project_to_simd(dest)?;
 
                 let index =
-                    generic_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
+                    generic_args[2].expect_const().to_value().valtree.unwrap_branch();
                 let index_len = index.len();
 
                 assert_eq!(left_len, right_len);

From 4fcae667d6ff477a274eaecc6437d8dc828b9265 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Rakic?= 
Date: Thu, 30 Jan 2025 17:10:19 +0000
Subject: [PATCH 287/342] normalize long-type.txt in tests

this allows compare-mode to share the same subdirectory and removes
differences due to that
---
 .../ui/diagnostic-width/long-E0308.ascii.stderr  | 16 ++++++++--------
 tests/ui/diagnostic-width/long-E0308.rs          |  4 +++-
 .../diagnostic-width/long-E0308.unicode.stderr   | 16 ++++++++--------
 tests/ui/diagnostic-width/non-copy-type-moved.rs |  4 +++-
 .../diagnostic-width/non-copy-type-moved.stderr  |  4 ++--
 .../secondary-label-with-long-type.rs            |  4 +++-
 .../secondary-label-with-long-type.stderr        |  4 ++--
 ...inite-instantiation-struct-tail-ice-114484.rs |  3 +++
 ...e-instantiation-struct-tail-ice-114484.stderr | 10 +++++-----
 tests/ui/infinite/infinite-instantiation.rs      |  3 ++-
 tests/ui/infinite/infinite-instantiation.stderr  |  6 +++---
 .../issue-37311-type-length-limit/issue-37311.rs |  3 ++-
 .../issue-37311.stderr                           |  6 +++---
 tests/ui/issues/issue-67552.rs                   |  3 ++-
 tests/ui/issues/issue-67552.stderr               |  6 +++---
 tests/ui/issues/issue-8727.rs                    |  3 ++-
 tests/ui/issues/issue-8727.stderr                |  8 ++++----
 tests/ui/recursion/recursion.rs                  |  3 ++-
 tests/ui/recursion/recursion.stderr              |  6 +++---
 tests/ui/traits/on_unimplemented_long_types.rs   |  3 ++-
 .../ui/traits/on_unimplemented_long_types.stderr |  6 +++---
 tests/ui/type_length_limit.rs                    |  3 +++
 tests/ui/type_length_limit.stderr                |  4 ++--
 23 files changed, 73 insertions(+), 55 deletions(-)

diff --git a/tests/ui/diagnostic-width/long-E0308.ascii.stderr b/tests/ui/diagnostic-width/long-E0308.ascii.stderr
index d45d6bf329bd..3053e37a87a7 100644
--- a/tests/ui/diagnostic-width/long-E0308.ascii.stderr
+++ b/tests/ui/diagnostic-width/long-E0308.ascii.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/long-E0308.rs:46:9
+  --> $DIR/long-E0308.rs:48:9
    |
 LL |        let x: Atype<
    |  _____________-
@@ -20,11 +20,11 @@ LL |  |     ))))))))))))))))))))))))))))));
    |
    = note: expected struct `Atype, ...>`
                 found enum `Result, ...>`
-   = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
 error[E0308]: mismatched types
-  --> $DIR/long-E0308.rs:59:26
+  --> $DIR/long-E0308.rs:61:26
    |
 LL |       ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O...
    |  __________________________^
@@ -36,11 +36,11 @@ LL | |     ))))))))))))))))))))))));
    |
    = note: expected enum `Option>`
               found enum `Result, ...>`
-   = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
 error[E0308]: mismatched types
-  --> $DIR/long-E0308.rs:90:9
+  --> $DIR/long-E0308.rs:92:9
    |
 LL |       let x: Atype<
    |  ____________-
@@ -56,11 +56,11 @@ LL | |     > = ();
    |
    = note: expected struct `Atype, ...>`
            found unit type `()`
-   = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
 error[E0308]: mismatched types
-  --> $DIR/long-E0308.rs:93:17
+  --> $DIR/long-E0308.rs:95:17
    |
 LL |       let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O...
    |  ____________--___^
@@ -74,7 +74,7 @@ LL | |     ))))))))))))))))))))))));
    |
    = note: expected unit type `()`
                    found enum `Result, ...>`
-   = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
 error: aborting due to 4 previous errors
diff --git a/tests/ui/diagnostic-width/long-E0308.rs b/tests/ui/diagnostic-width/long-E0308.rs
index 939872260209..26383d9418dd 100644
--- a/tests/ui/diagnostic-width/long-E0308.rs
+++ b/tests/ui/diagnostic-width/long-E0308.rs
@@ -1,7 +1,9 @@
 //@ revisions: ascii unicode
 //@[ascii] compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes
 //@[unicode] compile-flags: -Zunstable-options --json=diagnostic-unicode --diagnostic-width=60 -Zwrite-long-types-to-disk=yes
-//@ normalize-stderr: "long-type-\d+" -> "long-type-hash"
+
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'"
 
 mod a {
     // Force the "short path for unique types" machinery to trip up
diff --git a/tests/ui/diagnostic-width/long-E0308.unicode.stderr b/tests/ui/diagnostic-width/long-E0308.unicode.stderr
index 3e8d881d7a6b..50b386325b93 100644
--- a/tests/ui/diagnostic-width/long-E0308.unicode.stderr
+++ b/tests/ui/diagnostic-width/long-E0308.unicode.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-   ╭▸ $DIR/long-E0308.rs:46:9
+   ╭▸ $DIR/long-E0308.rs:48:9
    │
 LL │        let x: Atype<
    │ ┌─────────────┘
@@ -20,11 +20,11 @@ LL │  ┃     ))))))))))))))))))))))))))))));
    │
    ├ note: expected struct `Atype, ...>`
    │            found enum `Result, ...>`
-   ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt'
+   ├ note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    ╰ note: consider using `--verbose` to print the full type name to the console
 
 error[E0308]: mismatched types
-   ╭▸ $DIR/long-E0308.rs:59:26
+   ╭▸ $DIR/long-E0308.rs:61:26
    │
 LL │       ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(…
    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@@ -36,11 +36,11 @@ LL │ ┃     ))))))))))))))))))))))));
    │
    ├ note: expected enum `Option>`
    │          found enum `Result, ...>`
-   ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt'
+   ├ note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    ╰ note: consider using `--verbose` to print the full type name to the console
 
 error[E0308]: mismatched types
-   ╭▸ $DIR/long-E0308.rs:90:9
+   ╭▸ $DIR/long-E0308.rs:92:9
    │
 LL │       let x: Atype<
    │ ┌────────────┘
@@ -56,11 +56,11 @@ LL │ │     > = ();
    │
    ├ note: expected struct `Atype, ...>`
    │       found unit type `()`
-   ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt'
+   ├ note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    ╰ note: consider using `--verbose` to print the full type name to the console
 
 error[E0308]: mismatched types
-   ╭▸ $DIR/long-E0308.rs:93:17
+   ╭▸ $DIR/long-E0308.rs:95:17
    │
 LL │       let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(…
    │ ┏━━━━━━━━━━━━┬─━━━┛
@@ -74,7 +74,7 @@ LL │ ┃     ))))))))))))))))))))))));
    │
    ├ note: expected unit type `()`
    │               found enum `Result, ...>`
-   ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt'
+   ├ note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    ╰ note: consider using `--verbose` to print the full type name to the console
 
 error: aborting due to 4 previous errors
diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.rs b/tests/ui/diagnostic-width/non-copy-type-moved.rs
index a5593ad7b2a3..a220c62775eb 100644
--- a/tests/ui/diagnostic-width/non-copy-type-moved.rs
+++ b/tests/ui/diagnostic-width/non-copy-type-moved.rs
@@ -1,5 +1,7 @@
 //@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes
-//@ normalize-stderr: "long-type-\d+" -> "long-type-hash"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'"
+
 type A = (String, String, String, String);
 type B = (A, A, A, A);
 type C = (B, B, B, B);
diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.stderr b/tests/ui/diagnostic-width/non-copy-type-moved.stderr
index da9385a5b4dc..889a2b3666dd 100644
--- a/tests/ui/diagnostic-width/non-copy-type-moved.stderr
+++ b/tests/ui/diagnostic-width/non-copy-type-moved.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `x`
-  --> $DIR/non-copy-type-moved.rs:14:14
+  --> $DIR/non-copy-type-moved.rs:16:14
    |
 LL | fn foo(x: D) {
    |        - move occurs because `x` has type `((..., ..., ..., ...), ..., ..., ...)`, which does not implement the `Copy` trait
@@ -8,7 +8,7 @@ LL |     let _a = x;
 LL |     let _b = x;
    |              ^ value used here after move
    |
-   = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/non-copy-type-moved/non-copy-type-moved.long-type-hash.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 help: consider cloning the value if the performance cost is acceptable
    |
diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs
index 6ed600c48aca..4caa94244250 100644
--- a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs
+++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs
@@ -1,5 +1,7 @@
 //@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes
-//@ normalize-stderr: "long-type-\d+" -> "long-type-hash"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'"
+
 type A = (i32, i32, i32, i32);
 type B = (A, A, A, A);
 type C = (B, B, B, B);
diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr
index 1e8904551569..346b112019fa 100644
--- a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr
+++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/secondary-label-with-long-type.rs:9:9
+  --> $DIR/secondary-label-with-long-type.rs:11:9
    |
 LL |     let () = x;
    |         ^^   - this expression has type `((..., ..., ..., ...), ..., ..., ...)`
@@ -8,7 +8,7 @@ LL |     let () = x;
    |
    = note:  expected tuple `((..., ..., ..., ...), ..., ..., ...)`
            found unit type `()`
-   = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/secondary-label-with-long-type/secondary-label-with-long-type.long-type-hash.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs
index e28b8f373dab..62fc079d9d97 100644
--- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs
+++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs
@@ -3,6 +3,9 @@
 //@ error-pattern: reached the recursion limit while instantiating
 //@ error-pattern: reached the recursion limit finding the struct tail
 
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
+
 // Regression test for #114484: This used to ICE during monomorphization, because we treated
 // ` as Pointee>::Metadata` as a rigid projection after reaching the recursion
 // limit when finding the struct tail.
diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr
index 7c961b79c0ca..b67e000bf74a 100644
--- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr
+++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr
@@ -18,7 +18,7 @@ error: reached the recursion limit finding the struct tail for `[u8; 256]`
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>>`
-  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:26:18
+  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18
    |
 LL |         unsafe { virtualize_my_trait(L, self) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -43,7 +43,7 @@ error: reached the recursion limit finding the struct tail for `SomeData<256>`
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>>`
-  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:26:18
+  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18
    |
 LL |         unsafe { virtualize_my_trait(L, self) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -68,7 +68,7 @@ error: reached the recursion limit finding the struct tail for `VirtualWrapper, 0>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>>`
-  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:26:18
+  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18
    |
 LL |         unsafe { virtualize_my_trait(L, self) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -76,11 +76,11 @@ LL |         unsafe { virtualize_my_trait(L, self) }
 error: reached the recursion limit while instantiating `, 1>, 1>, 1>, 1> as MyTrait>::virtualize`
    |
 note: ` as MyTrait>::virtualize` defined here
-  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:5
+  --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:28:5
    |
 LL |     fn virtualize(&self) -> &dyn MyTrait {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: the full type name has been written to '$TEST_BUILD_DIR/infinite/infinite-instantiation-struct-tail-ice-114484/infinite-instantiation-struct-tail-ice-114484.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: aborting due to 13 previous errors
 
diff --git a/tests/ui/infinite/infinite-instantiation.rs b/tests/ui/infinite/infinite-instantiation.rs
index 7e1bff6b1242..d5cb8e79592f 100644
--- a/tests/ui/infinite/infinite-instantiation.rs
+++ b/tests/ui/infinite/infinite-instantiation.rs
@@ -1,5 +1,6 @@
 //@ build-fail
-//@ normalize-stderr: ".nll/" -> "/"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
 
 trait ToOpt: Sized {
     fn to_option(&self) -> Option;
diff --git a/tests/ui/infinite/infinite-instantiation.stderr b/tests/ui/infinite/infinite-instantiation.stderr
index 43d267fa46bf..71c745cf5eb8 100644
--- a/tests/ui/infinite/infinite-instantiation.stderr
+++ b/tests/ui/infinite/infinite-instantiation.stderr
@@ -1,15 +1,15 @@
 error: reached the recursion limit while instantiating `function::>>>>>`
-  --> $DIR/infinite-instantiation.rs:22:9
+  --> $DIR/infinite-instantiation.rs:23:9
    |
 LL |         function(counter - 1, t.to_option());
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `function` defined here
-  --> $DIR/infinite-instantiation.rs:20:1
+  --> $DIR/infinite-instantiation.rs:21:1
    |
 LL | fn function(counter: usize, t: T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: the full type name has been written to '$TEST_BUILD_DIR/infinite/infinite-instantiation/infinite-instantiation.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs
index edf4f2fce26a..ebaf244ac9c2 100644
--- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs
+++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs
@@ -1,5 +1,6 @@
 //@ build-fail
-//@ normalize-stderr: ".nll/" -> "/"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
 
 trait Mirror {
     type Image;
diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr
index 84ed97572b3a..fbbf80021bee 100644
--- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr
+++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr
@@ -1,15 +1,15 @@
 error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse`
-  --> $DIR/issue-37311.rs:17:9
+  --> $DIR/issue-37311.rs:18:9
    |
 LL |         (self, self).recurse();
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `::recurse` defined here
-  --> $DIR/issue-37311.rs:16:5
+  --> $DIR/issue-37311.rs:17:5
    |
 LL |     fn recurse(&self) {
    |     ^^^^^^^^^^^^^^^^^
-   = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-37311-type-length-limit/issue-37311/issue-37311.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-67552.rs b/tests/ui/issues/issue-67552.rs
index 343ae4f262fc..8c7e95bd2e3e 100644
--- a/tests/ui/issues/issue-67552.rs
+++ b/tests/ui/issues/issue-67552.rs
@@ -1,6 +1,7 @@
 //@ build-fail
 //@ compile-flags: -Copt-level=0
-//@ normalize-stderr: ".nll/" -> "/"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
 
 fn main() {
     rec(Empty);
diff --git a/tests/ui/issues/issue-67552.stderr b/tests/ui/issues/issue-67552.stderr
index 1a8d7248b450..f94cd78c8701 100644
--- a/tests/ui/issues/issue-67552.stderr
+++ b/tests/ui/issues/issue-67552.stderr
@@ -1,17 +1,17 @@
 error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut ...>`
-  --> $DIR/issue-67552.rs:29:9
+  --> $DIR/issue-67552.rs:30:9
    |
 LL |         rec(identity(&mut it))
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `rec` defined here
-  --> $DIR/issue-67552.rs:22:1
+  --> $DIR/issue-67552.rs:23:1
    |
 LL | / fn rec(mut it: T)
 LL | | where
 LL | |     T: Iterator,
    | |________________^
-   = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-67552/issue-67552.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-8727.rs b/tests/ui/issues/issue-8727.rs
index b824be7c12fe..7767729109ea 100644
--- a/tests/ui/issues/issue-8727.rs
+++ b/tests/ui/issues/issue-8727.rs
@@ -2,7 +2,8 @@
 // recursions.
 
 //@ build-fail
-//@ normalize-stderr: ".nll/" -> "/"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
 
 fn generic() { //~ WARN function cannot return without recursing
     generic::>();
diff --git a/tests/ui/issues/issue-8727.stderr b/tests/ui/issues/issue-8727.stderr
index 9af598fe43f5..22286eb8d7be 100644
--- a/tests/ui/issues/issue-8727.stderr
+++ b/tests/ui/issues/issue-8727.stderr
@@ -1,5 +1,5 @@
 warning: function cannot return without recursing
-  --> $DIR/issue-8727.rs:7:1
+  --> $DIR/issue-8727.rs:8:1
    |
 LL | fn generic() {
    | ^^^^^^^^^^^^^^^ cannot return without recursing
@@ -10,17 +10,17 @@ LL |     generic::>();
    = note: `#[warn(unconditional_recursion)]` on by default
 
 error: reached the recursion limit while instantiating `generic::>>>>>`
-  --> $DIR/issue-8727.rs:8:5
+  --> $DIR/issue-8727.rs:9:5
    |
 LL |     generic::>();
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `generic` defined here
-  --> $DIR/issue-8727.rs:7:1
+  --> $DIR/issue-8727.rs:8:1
    |
 LL | fn generic() {
    | ^^^^^^^^^^^^^^^
-   = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-8727/issue-8727.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: aborting due to 1 previous error; 1 warning emitted
 
diff --git a/tests/ui/recursion/recursion.rs b/tests/ui/recursion/recursion.rs
index f3c633983b19..ce56fe974b7c 100644
--- a/tests/ui/recursion/recursion.rs
+++ b/tests/ui/recursion/recursion.rs
@@ -1,6 +1,7 @@
 //@ build-fail
 //@ compile-flags:-C overflow-checks=off
-//@ normalize-stderr: ".nll/" -> "/"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
 
 enum Nil {NilValue}
 struct Cons {head:isize, tail:T}
diff --git a/tests/ui/recursion/recursion.stderr b/tests/ui/recursion/recursion.stderr
index 7a9f04d4bd6d..cb9f67ba7418 100644
--- a/tests/ui/recursion/recursion.stderr
+++ b/tests/ui/recursion/recursion.stderr
@@ -1,15 +1,15 @@
 error: reached the recursion limit while instantiating `test::>>>>>`
-  --> $DIR/recursion.rs:18:11
+  --> $DIR/recursion.rs:19:11
    |
 LL |     _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: `test` defined here
-  --> $DIR/recursion.rs:16:1
+  --> $DIR/recursion.rs:17:1
    |
 LL | fn test (n:isize, i:isize, first:T, second:T) ->isize {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/recursion/recursion.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/on_unimplemented_long_types.rs b/tests/ui/traits/on_unimplemented_long_types.rs
index 98749b8db7ae..c652b71e51ac 100644
--- a/tests/ui/traits/on_unimplemented_long_types.rs
+++ b/tests/ui/traits/on_unimplemented_long_types.rs
@@ -1,5 +1,6 @@
 //@ compile-flags: --diagnostic-width=60 -Z write-long-types-to-disk=yes
-//@ normalize-stderr: "long-type-\d+" -> "long-type-hash"
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'"
 
 pub fn foo() -> impl std::fmt::Display {
     //~^ ERROR doesn't implement `std::fmt::Display`
diff --git a/tests/ui/traits/on_unimplemented_long_types.stderr b/tests/ui/traits/on_unimplemented_long_types.stderr
index bddc5695696b..5722f4006ffd 100644
--- a/tests/ui/traits/on_unimplemented_long_types.stderr
+++ b/tests/ui/traits/on_unimplemented_long_types.stderr
@@ -1,5 +1,5 @@
 error[E0277]: `Option>>` doesn't implement `std::fmt::Display`
-  --> $DIR/on_unimplemented_long_types.rs:4:17
+  --> $DIR/on_unimplemented_long_types.rs:5:17
    |
 LL |   pub fn foo() -> impl std::fmt::Display {
    |                   ^^^^^^^^^^^^^^^^^^^^^^ `Option>>` cannot be formatted with the default formatter
@@ -13,11 +13,11 @@ LL | |         ))))))))))),
 LL | |     )))))))))))
    | |_______________- return type was inferred to be `Option>>` here
    |
-   = note: the full name for the type has been written to '$TEST_BUILD_DIR/traits/on_unimplemented_long_types/on_unimplemented_long_types.long-type-hash.txt'
+   = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
    = help: the trait `std::fmt::Display` is not implemented for `Option>>`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: the full name for the type has been written to '$TEST_BUILD_DIR/traits/on_unimplemented_long_types/on_unimplemented_long_types.long-type-hash.txt'
+   = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/type_length_limit.rs b/tests/ui/type_length_limit.rs
index 87f5ffd76d7a..b629455aced7 100644
--- a/tests/ui/type_length_limit.rs
+++ b/tests/ui/type_length_limit.rs
@@ -2,6 +2,9 @@
 //@ compile-flags: -Copt-level=0 -Zenforce-type-length-limit
 //~^^ ERROR reached the type-length limit
 
+// The regex below normalizes the long type file name to make it suitable for compare-modes.
+//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'"
+
 // Test that the type length limit can be changed.
 // The exact type depends on optimizations, so disable them.
 
diff --git a/tests/ui/type_length_limit.stderr b/tests/ui/type_length_limit.stderr
index 83353547d34d..d913b661c6f0 100644
--- a/tests/ui/type_length_limit.stderr
+++ b/tests/ui/type_length_limit.stderr
@@ -1,11 +1,11 @@
 error: reached the type-length limit while instantiating `std::mem::drop::>`
-  --> $DIR/type_length_limit.rs:32:5
+  --> $DIR/type_length_limit.rs:35:5
    |
 LL |     drop::>(None);
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding a `#![type_length_limit="4010"]` attribute to your crate
-   = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
+   = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt'
 
 error: reached the type-length limit while instantiating `<{closure@rt::lang_start<()>::{closure#0}} as FnMut<()>>::call_mut`
    |

From 0055fb92db2fcfb8c74d1bf9063f14f65d5dcce0 Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Mon, 27 Jan 2025 11:16:19 +0000
Subject: [PATCH 288/342] check the types in `ty::Value` to value conversion

and remove `ty::Const::try_to_scalar` because it becomes redundant
---
 compiler/rustc_middle/src/ty/consts.rs        |  7 ---
 .../rustc_middle/src/ty/consts/valtree.rs     | 12 +++++-
 .../cfi/typeid/itanium_cxx_abi/transform.rs   | 10 ++++-
 .../src/error_reporting/infer/mod.rs          | 10 ++---
 tests/crashes/131102.rs                       |  4 --
 tests/ui/consts/bad-array-size-in-type-err.rs | 11 +++++
 .../consts/bad-array-size-in-type-err.stderr  | 43 ++++++++++++++++++-
 7 files changed, 75 insertions(+), 22 deletions(-)
 delete mode 100644 tests/crashes/131102.rs

diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 6875532e28aa..4ad5d8443b2f 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -5,7 +5,6 @@ use rustc_error_messages::MultiSpan;
 use rustc_macros::HashStable;
 use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
 
-use crate::mir::interpret::Scalar;
 use crate::ty::{self, Ty, TyCtxt};
 
 mod int;
@@ -230,12 +229,6 @@ impl<'tcx> Const<'tcx> {
         }
     }
 
-    #[inline]
-    pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> {
-        let cv = self.try_to_value()?;
-        Some((cv.valtree.try_to_scalar()?, cv.ty))
-    }
-
     #[inline]
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option {
         self.try_to_value()?.try_to_target_usize(tcx)
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index 425b68a951c6..d914b7576dc4 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -117,9 +117,13 @@ pub struct Value<'tcx> {
 impl<'tcx> Value<'tcx> {
     /// Attempts to extract the raw bits from the constant.
     ///
-    /// Fails if the value can't be represented as bits (e.g. because it is an aggregate).
+    /// Fails if the value can't be represented as bits (e.g. because it is a reference
+    /// or an aggregate).
     #[inline]
     pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option {
+        let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else {
+            return None;
+        };
         let scalar = self.valtree.try_to_scalar_int()?;
         let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty);
         let size = tcx.layout_of(input).ok()?.size;
@@ -127,10 +131,16 @@ impl<'tcx> Value<'tcx> {
     }
 
     pub fn try_to_bool(self) -> Option {
+        if !self.ty.is_bool() {
+            return None;
+        }
         self.valtree.try_to_scalar_int()?.try_to_bool().ok()
     }
 
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option {
+        if !self.ty.is_usize() {
+            return None;
+        }
         self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
     }
 }
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 9c6186d68828..b711c238d594 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -51,8 +51,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> {
     // Transforms a ty:Ty for being encoded and used in the substitution dictionary.
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match t.kind() {
-            ty::Array(..)
-            | ty::Closure(..)
+            ty::Closure(..)
             | ty::Coroutine(..)
             | ty::CoroutineClosure(..)
             | ty::CoroutineWitness(..)
@@ -67,6 +66,13 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> {
             | ty::Tuple(..)
             | ty::UnsafeBinder(_) => t.super_fold_with(self),
 
+            // Don't transform the type of the array length and keep it as `usize`.
+            // This is required for `try_to_target_usize` to work correctly.
+            &ty::Array(inner, len) => {
+                let inner = self.fold_ty(inner);
+                Ty::new_array_with_const_len(self.tcx, inner, len)
+            }
+
             ty::Bool => {
                 if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
                     // Note: on all platforms that Rust's currently supports, its size and alignment
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 9eacd377361a..117b6a764257 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -2023,14 +2023,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             _ => None,
         };
         if let Some(tykind) = tykind
-            && let hir::TyKind::Array(_, length) = tykind
-            && let Some((scalar, ty)) = sz.found.try_to_scalar()
-            && ty == self.tcx.types.usize
+            && let hir::TyKind::Array(_, length_arg) = tykind
+            && let Some(length_val) = sz.found.try_to_target_usize(self.tcx)
         {
-            let span = length.span();
             Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength {
-                span,
-                length: scalar.to_target_usize(&self.tcx).unwrap(),
+                span: length_arg.span(),
+                length: length_val,
             })
         } else {
             None
diff --git a/tests/crashes/131102.rs b/tests/crashes/131102.rs
deleted file mode 100644
index 12b35f8d1b2a..000000000000
--- a/tests/crashes/131102.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-//@ known-bug: #131102
-pub struct Blorb([String; N]);
-pub struct Wrap(Blorb<0>);
-pub const fn i(_: Wrap) {}
diff --git a/tests/ui/consts/bad-array-size-in-type-err.rs b/tests/ui/consts/bad-array-size-in-type-err.rs
index cb02ad3205db..0490a9d3620e 100644
--- a/tests/ui/consts/bad-array-size-in-type-err.rs
+++ b/tests/ui/consts/bad-array-size-in-type-err.rs
@@ -8,3 +8,14 @@ fn main() {
     //~^ ERROR mismatched types
     //~| ERROR the constant `2` is not of type `usize`
 }
+
+fn iter(val: BadArraySize::<2>) {
+    for _ in val.arr {}
+    //~^ ERROR the constant `2` is not of type `usize`
+    //~| ERROR `[i32; 2]` is not an iterator
+}
+
+// issue #131102
+pub struct Blorb([String; N]); //~ ERROR the constant `N` is not of type `usize`
+pub struct Wrap(Blorb<0>);
+pub const fn i(_: Wrap) {} //~ ERROR destructor of `Wrap` cannot be evaluated at compile-time
diff --git a/tests/ui/consts/bad-array-size-in-type-err.stderr b/tests/ui/consts/bad-array-size-in-type-err.stderr
index c3ff216432ee..84e16f8d931e 100644
--- a/tests/ui/consts/bad-array-size-in-type-err.stderr
+++ b/tests/ui/consts/bad-array-size-in-type-err.stderr
@@ -6,6 +6,14 @@ LL |     arr: [i32; N],
    |
    = note: the length of array `[i32; N]` must be type `usize`
 
+error: the constant `N` is not of type `usize`
+  --> $DIR/bad-array-size-in-type-err.rs:19:32
+   |
+LL | pub struct Blorb([String; N]);
+   |                                ^^^^^^^^^^^ expected `usize`, found `u16`
+   |
+   = note: the length of array `[String; N]` must be type `usize`
+
 error[E0308]: mismatched types
   --> $DIR/bad-array-size-in-type-err.rs:7:38
    |
@@ -20,6 +28,37 @@ LL |     let _ = BadArraySize::<2> { arr: [0, 0, 0] };
    |
    = note: the length of array `[i32; 2]` must be type `usize`
 
-error: aborting due to 3 previous errors
+error: the constant `2` is not of type `usize`
+  --> $DIR/bad-array-size-in-type-err.rs:13:14
+   |
+LL |     for _ in val.arr {}
+   |              ^^^^^^^ expected `usize`, found `u8`
+   |
+   = note: the length of array `[i32; 2]` must be type `usize`
 
-For more information about this error, try `rustc --explain E0308`.
+error[E0277]: `[i32; 2]` is not an iterator
+  --> $DIR/bad-array-size-in-type-err.rs:13:14
+   |
+LL |     for _ in val.arr {}
+   |              ^^^^^^^ `[i32; 2]` is not an iterator; try calling `.into_iter()` or `.iter()`
+   |
+   = help: the trait `IntoIterator` is not implemented for `[i32; 2]`
+   = help: the following other types implement trait `IntoIterator`:
+             &[T; N]
+             &[T]
+             &mut [T; N]
+             &mut [T]
+             [T; N]
+
+error[E0493]: destructor of `Wrap` cannot be evaluated at compile-time
+  --> $DIR/bad-array-size-in-type-err.rs:21:16
+   |
+LL | pub const fn i(_: Wrap) {}
+   |                ^         - value is dropped here
+   |                |
+   |                the destructor for this type cannot be evaluated in constant functions
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0277, E0308, E0493.
+For more information about an error, try `rustc --explain E0277`.

From ca3ff832e34cc32d93c9e940d8f6d3e4034203fa Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Thu, 30 Jan 2025 17:22:45 +0100
Subject: [PATCH 289/342] add comments

---
 compiler/rustc_const_eval/src/const_eval/valtrees.rs      | 3 ++-
 compiler/rustc_middle/src/ty/consts.rs                    | 6 ++++++
 compiler/rustc_middle/src/ty/print/pretty.rs              | 1 +
 compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs | 1 +
 4 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 4ff8aa9a3b41..4d625f76aba2 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -272,7 +272,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
 
 /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
 /// construction has finished.
-// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
+// FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
+// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
 #[instrument(skip(tcx), level = "debug", ret)]
 pub fn valtree_to_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 4ad5d8443b2f..d77fb1cc91e8 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -222,6 +222,8 @@ impl<'tcx> Const<'tcx> {
     }
 
     /// Attempts to convert to a value.
+    ///
+    /// Note that this does not evaluate the constant.
     pub fn try_to_value(self) -> Option> {
         match self.kind() {
             ty::ConstKind::Value(cv) => Some(cv),
@@ -229,6 +231,10 @@ impl<'tcx> Const<'tcx> {
         }
     }
 
+    /// Convenience method to extract the value of a usize constant,
+    /// useful to get the length of an array type.
+    ///
+    /// Note that this does not evaluate the constant.
     #[inline]
     pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option {
         self.try_to_value()?.try_to_target_usize(tcx)
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 19a39b3a29ea..018fcc66aeed 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1785,6 +1785,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         Ok(())
     }
 
+    // FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
     fn pretty_print_const_valtree(
         &mut self,
         valtree: ty::ValTree<'tcx>,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index c490e3885d4d..cc6d69710e4c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -214,6 +214,7 @@ impl<'tcx> ConstToPat<'tcx> {
     }
 
     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
+    // FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
     #[instrument(skip(self), level = "debug")]
     fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box> {
         let span = self.span;

From d98b99af56e1260f520102a93f198ffe47793722 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Sat, 11 Jan 2025 19:31:28 +0000
Subject: [PATCH 290/342] More assertions, tests, and miri coverage

---
 compiler/rustc_codegen_ssa/src/meth.rs        |  2 +
 .../rustc_const_eval/src/interpret/call.rs    | 45 ++++++++-------
 .../rustc_const_eval/src/interpret/cast.rs    | 54 +++++++++---------
 .../src/traits/vtable.rs                      | 57 ++++++++-----------
 src/tools/miri/tests/pass/dyn-upcast.rs       | 52 +++++++++++++++++
 src/tools/miri/tests/pass/dyn-upcast.stdout   |  2 +
 ...ltiple-supertraits-modulo-binder-vtable.rs |  2 +
 ...le-supertraits-modulo-binder-vtable.stderr |  4 +-
 .../multiple-supertraits-modulo-binder.rs     |  2 +
 ...supertraits-modulo-normalization-vtable.rs |  3 +-
 ...rtraits-modulo-normalization-vtable.stderr |  4 +-
 ...ltiple-supertraits-modulo-normalization.rs |  2 +
 .../supertraits-modulo-inner-binder.rs        | 30 ++++++++++
 13 files changed, 173 insertions(+), 86 deletions(-)
 create mode 100644 tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs

diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs
index 886951855401..399c592432ac 100644
--- a/compiler/rustc_codegen_ssa/src/meth.rs
+++ b/compiler/rustc_codegen_ssa/src/meth.rs
@@ -80,6 +80,8 @@ fn dyn_trait_in_self<'tcx>(
         if let GenericArgKind::Type(ty) = arg.unpack()
             && let ty::Dynamic(data, _, _) = ty.kind()
         {
+            // FIXME(arbitrary_self_types): This is likely broken for receivers which
+            // have a "non-self" trait objects as a generic argument.
             return data
                 .principal()
                 .map(|principal| tcx.instantiate_bound_regions_with_erased(principal));
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs
index e6a34193c9d6..e2e6e16d8a71 100644
--- a/compiler/rustc_const_eval/src/interpret/call.rs
+++ b/compiler/rustc_const_eval/src/interpret/call.rs
@@ -5,6 +5,7 @@ use std::borrow::Cow;
 
 use either::{Left, Right};
 use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
+use rustc_hir::def_id::DefId;
 use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
 use rustc_middle::{bug, mir, span_bug};
@@ -693,25 +694,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 trace!("Virtual call dispatches to {fn_inst:#?}");
                 // We can also do the lookup based on `def_id` and `dyn_ty`, and check that that
                 // produces the same result.
-                if cfg!(debug_assertions) {
-                    let tcx = *self.tcx;
-
-                    let trait_def_id = tcx.trait_of_item(def_id).unwrap();
-                    let virtual_trait_ref =
-                        ty::TraitRef::from_method(tcx, trait_def_id, instance.args);
-                    let existential_trait_ref =
-                        ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
-                    let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
-
-                    let concrete_method = Instance::expect_resolve_for_vtable(
-                        tcx,
-                        self.typing_env,
-                        def_id,
-                        instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
-                        self.cur_span(),
-                    );
-                    assert_eq!(fn_inst, concrete_method);
-                }
+                self.assert_virtual_instance_matches_concrete(dyn_ty, def_id, instance, fn_inst);
 
                 // Adjust receiver argument. Layout can be any (thin) ptr.
                 let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty);
@@ -744,6 +727,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         }
     }
 
+    fn assert_virtual_instance_matches_concrete(
+        &self,
+        dyn_ty: Ty<'tcx>,
+        def_id: DefId,
+        virtual_instance: ty::Instance<'tcx>,
+        concrete_instance: ty::Instance<'tcx>,
+    ) {
+        let tcx = *self.tcx;
+
+        let trait_def_id = tcx.trait_of_item(def_id).unwrap();
+        let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args);
+        let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
+        let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
+
+        let concrete_method = Instance::expect_resolve_for_vtable(
+            tcx,
+            self.typing_env,
+            def_id,
+            virtual_instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
+            self.cur_span(),
+        );
+        assert_eq!(concrete_instance, concrete_method);
+    }
+
     /// Initiate a tail call to this function -- popping the current stack frame, pushing the new
     /// stack frame and initializing the arguments.
     pub(super) fn init_fn_tail_call(
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 52bc2af928dc..e110c155da08 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -414,35 +414,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
                 // Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces
                 // our destination trait.
-                if cfg!(debug_assertions) {
-                    let vptr_entry_idx =
-                        self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
-                    let vtable_entries = self.vtable_entries(data_a.principal(), ty);
-                    if let Some(entry_idx) = vptr_entry_idx {
-                        let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
-                            vtable_entries.get(entry_idx)
-                        else {
-                            span_bug!(
-                                self.cur_span(),
-                                "invalid vtable entry index in {} -> {} upcast",
-                                src_pointee_ty,
-                                dest_pointee_ty
-                            );
-                        };
-                        let erased_trait_ref =
-                            ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
-                        assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
-                            erased_trait_ref,
-                            self.tcx.instantiate_bound_regions_with_erased(b)
-                        )));
-                    } else {
-                        // In this case codegen would keep using the old vtable. We don't want to do
-                        // that as it has the wrong trait. The reason codegen can do this is that
-                        // one vtable is a prefix of the other, so we double-check that.
-                        let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
-                        assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
+                let vptr_entry_idx =
+                    self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty));
+                let vtable_entries = self.vtable_entries(data_a.principal(), ty);
+                if let Some(entry_idx) = vptr_entry_idx {
+                    let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) =
+                        vtable_entries.get(entry_idx)
+                    else {
+                        span_bug!(
+                            self.cur_span(),
+                            "invalid vtable entry index in {} -> {} upcast",
+                            src_pointee_ty,
+                            dest_pointee_ty
+                        );
                     };
-                }
+                    let erased_trait_ref =
+                        ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref);
+                    assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env(
+                        erased_trait_ref,
+                        self.tcx.instantiate_bound_regions_with_erased(b)
+                    )));
+                } else {
+                    // In this case codegen would keep using the old vtable. We don't want to do
+                    // that as it has the wrong trait. The reason codegen can do this is that
+                    // one vtable is a prefix of the other, so we double-check that.
+                    let vtable_entries_b = self.vtable_entries(data_b.principal(), ty);
+                    assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b);
+                };
 
                 // Get the destination trait vtable and return that.
                 let new_vptr = self.get_vtable_ptr(ty, data_b)?;
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index abdf5df6f728..000e6a765d35 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -2,8 +2,6 @@ use std::fmt::Debug;
 use std::ops::ControlFlow;
 
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::TyCtxtInferExt;
-use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::util::PredicateSet;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
@@ -14,7 +12,7 @@ use rustc_span::DUMMY_SP;
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
 
-use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
+use crate::traits::{impossible_predicates, is_vtable_safe_method};
 
 #[derive(Clone, Debug)]
 pub enum VtblSegment<'tcx> {
@@ -228,6 +226,11 @@ fn vtable_entries<'tcx>(
     trait_ref: ty::TraitRef<'tcx>,
 ) -> &'tcx [VtblEntry<'tcx>] {
     debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
+    debug_assert_eq!(
+        tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
+        trait_ref,
+        "vtable trait ref should be normalized"
+    );
 
     debug!("vtable_entries({:?})", trait_ref);
 
@@ -305,6 +308,11 @@ fn vtable_entries<'tcx>(
 // for `Supertrait`'s methods in the vtable of `Subtrait`.
 pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
     debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
+    debug_assert_eq!(
+        tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
+        key,
+        "vtable trait ref should be normalized"
+    );
 
     let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
         bug!();
@@ -323,11 +331,9 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
                     vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
                 }
                 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
-                    if trait_refs_are_compatible(
-                        tcx,
-                        ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
-                        target_principal,
-                    ) {
+                    if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
+                        == target_principal
+                    {
                         return ControlFlow::Break(vptr_offset);
                     }
 
@@ -358,6 +364,12 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
     ),
 ) -> Option {
     debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
+    debug_assert_eq!(
+        tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
+        key,
+        "upcasting trait refs should be normalized"
+    );
+
     let (source, target) = key;
 
     // If the target principal is `None`, we can just return `None`.
@@ -384,11 +396,9 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
                 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
                     vptr_offset +=
                         tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
-                    if trait_refs_are_compatible(
-                        tcx,
-                        ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal),
-                        target_principal,
-                    ) {
+                    if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
+                        == target_principal
+                    {
                         if emit_vptr {
                             return ControlFlow::Break(Some(vptr_offset));
                         } else {
@@ -408,27 +418,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
     prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
 }
 
-fn trait_refs_are_compatible<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    vtable_principal: ty::ExistentialTraitRef<'tcx>,
-    target_principal: ty::ExistentialTraitRef<'tcx>,
-) -> bool {
-    if vtable_principal.def_id != target_principal.def_id {
-        return false;
-    }
-
-    let (infcx, param_env) =
-        tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
-    let ocx = ObligationCtxt::new(&infcx);
-    let source_principal = ocx.normalize(&ObligationCause::dummy(), param_env, vtable_principal);
-    let target_principal = ocx.normalize(&ObligationCause::dummy(), param_env, target_principal);
-    let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, target_principal, source_principal)
-    else {
-        return false;
-    };
-    ocx.select_all_or_error().is_empty()
-}
-
 pub(super) fn provide(providers: &mut Providers) {
     *providers = Providers {
         own_existential_vtable_entries,
diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs
index 61410f7c4e0b..f100c4d6a869 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.rs
+++ b/src/tools/miri/tests/pass/dyn-upcast.rs
@@ -10,6 +10,8 @@ fn main() {
     replace_vptr();
     vtable_nop_cast();
     drop_principal();
+    modulo_binder();
+    modulo_assoc();
 }
 
 fn vtable_nop_cast() {
@@ -482,3 +484,53 @@ fn drop_principal() {
     println!("before");
     drop(y);
 }
+
+// Test for .
+fn modulo_binder() {
+    trait Supertrait {
+        fn _print_numbers(&self, mem: &[usize; 100]) {
+            println!("{mem:?}");
+        }
+    }
+    impl Supertrait for () {}
+
+    trait Trait: Supertrait + Supertrait {
+        fn say_hello(&self, _: &usize) {
+            println!("Hello!");
+        }
+    }
+    impl Trait for () {}
+
+    (&() as &'static dyn for<'a> Trait<&'static (), &'a ()>
+        as &'static dyn Trait<&'static (), &'static ()>)
+        .say_hello(&0);
+}
+
+// Test for .
+fn modulo_assoc() {
+    trait Supertrait {
+        fn _print_numbers(&self, mem: &[usize; 100]) {
+            println!("{mem:?}");
+        }
+    }
+    impl Supertrait for () {}
+
+    trait Identity {
+        type Selff;
+    }
+    impl Identity for Selff {
+        type Selff = Selff;
+    }
+
+    trait Middle: Supertrait<()> + Supertrait {
+        fn say_hello(&self, _: &usize) {
+            println!("Hello!");
+        }
+    }
+    impl Middle for () {}
+
+    trait Trait: Middle<<() as Identity>::Selff> {}
+    impl Trait for () {}
+
+    (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0);
+}
diff --git a/src/tools/miri/tests/pass/dyn-upcast.stdout b/src/tools/miri/tests/pass/dyn-upcast.stdout
index edd99a114a11..379600db3d91 100644
--- a/src/tools/miri/tests/pass/dyn-upcast.stdout
+++ b/src/tools/miri/tests/pass/dyn-upcast.stdout
@@ -2,3 +2,5 @@ before
 goodbye
 before
 goodbye
+Hello!
+Hello!
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs
index b41cf2e9f267..796ddec46ac7 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs
@@ -1,5 +1,7 @@
 #![feature(rustc_attrs)]
 
+// Test for .
+
 trait Supertrait {
     fn _print_numbers(&self, mem: &[usize; 100]) {
     }
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
index 4bb7e1bdb6ad..24fa1650ca14 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr
@@ -5,7 +5,7 @@ error: vtable entries: [
            Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)),
            Method( Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)),
        ]
-  --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:18:1
+  --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:20:1
    |
 LL | type First = dyn for<'a> Trait<&'static (), &'a ()>;
    | ^^^^^^^^^^
@@ -17,7 +17,7 @@ error: vtable entries: [
            Method( as Supertrait<&()>>::_print_numbers - shim(reify)),
            Method( as Trait<&(), &()>>::say_hello - shim(reify)),
        ]
-  --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:22:1
+  --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:24:1
    |
 LL | type Second = dyn Trait<&'static (), &'static ()>;
    | ^^^^^^^^^^^
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs
index 7bc069f4f440..510a1471af29 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs
@@ -1,6 +1,8 @@
 //@ run-pass
 //@ check-run-results
 
+// Test for .
+
 #![feature(trait_upcasting)]
 
 trait Supertrait {
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs
index 22d6bf94d870..69a71859a5cc 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs
@@ -1,7 +1,8 @@
 #![feature(rustc_attrs)]
-
 #![feature(trait_upcasting)]
 
+// Test for .
+
 trait Supertrait {
     fn _print_numbers(&self, mem: &[usize; 100]) {
         println!("{mem:?}");
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
index 04b1afae7bec..757e2dc69390 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr
@@ -5,7 +5,7 @@ error: vtable entries: [
            Method(<() as Supertrait<()>>::_print_numbers),
            Method(<() as Middle<()>>::say_hello),
        ]
-  --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1
+  --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:30:1
    |
 LL | impl Trait for () {}
    | ^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ error: vtable entries: [
            Method( as Supertrait<()>>::_print_numbers - shim(reify)),
            Method( as Middle<()>>::say_hello - shim(reify)),
        ]
-  --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:33:1
+  --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:34:1
    |
 LL | type Virtual = dyn Middle<()>;
    | ^^^^^^^^^^^^
diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs
index fd0f62b4255a..c744e6e64f57 100644
--- a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs
+++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs
@@ -3,6 +3,8 @@
 
 #![feature(trait_upcasting)]
 
+// Test for .
+
 trait Supertrait {
     fn _print_numbers(&self, mem: &[usize; 100]) {
         println!("{mem:?}");
diff --git a/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs b/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs
new file mode 100644
index 000000000000..6cd74b6c7f75
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs
@@ -0,0 +1,30 @@
+//@ run-pass
+
+#![feature(trait_upcasting)]
+
+trait Super {
+    fn call(&self)
+    where
+        U: HigherRanked,
+    {
+    }
+}
+
+impl Super for () {}
+
+trait HigherRanked {}
+impl HigherRanked for for<'a> fn(&'a ()) {}
+
+trait Unimplemented {}
+impl HigherRanked for T {}
+
+trait Sub: Super + Super fn(&'a ())> {}
+impl Sub for () {}
+
+fn main() {
+    let a: &dyn Sub = &();
+    // `Super` and `Super fn(&'a ())>` have different
+    // vtables and we need to upcast to the latter!
+    let b: &dyn Super fn(&'a ())> = a;
+    b.call();
+}

From e831877c4cc18608405c3f2995c084bb3cfa284b Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Thu, 30 Jan 2025 18:08:45 +0000
Subject: [PATCH 291/342] Revert "[style 2024] Combine all last arg delimited
 exprs"

This reverts commit bed0c9d97f098b71f4968808ab16d9ba40bce49c.
---
 src/doc/style-guide/src/editions.md    |  4 --
 src/doc/style-guide/src/expressions.md | 55 ++++----------------------
 2 files changed, 7 insertions(+), 52 deletions(-)

diff --git a/src/doc/style-guide/src/editions.md b/src/doc/style-guide/src/editions.md
index b9a89c20cee4..19e62c4867c9 100644
--- a/src/doc/style-guide/src/editions.md
+++ b/src/doc/style-guide/src/editions.md
@@ -36,10 +36,6 @@ For a full history of changes in the Rust 2024 style edition, see the git
 history of the style guide. Notable changes in the Rust 2024 style edition
 include:
 
-- [#114764](https://github.com/rust-lang/rust/pull/114764) As the last member
-  of a delimited expression, delimited expressions are generally combinable,
-  regardless of the number of members. Previously only applied with exactly
-  one member (except for closures with explicit blocks).
 - Miscellaneous `rustfmt` bugfixes.
 - Use version-sort (sort `x8`, `x16`, `x32`, `x64`, `x128` in that order).
 - Change "ASCIIbetical" sort to Unicode-aware "non-lowercase before lowercase".
diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md
index 171a24cd89d7..12037b5992ec 100644
--- a/src/doc/style-guide/src/expressions.md
+++ b/src/doc/style-guide/src/expressions.md
@@ -818,11 +818,11 @@ E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not.
 
 ## Combinable expressions
 
-When the last argument in a function call is formatted across
-multiple-lines, format the outer call as if it were a single-line call,
+Where a function call has a single argument, and that argument is formatted
+across multiple-lines, format the outer call as if it were a single-line call,
 if the result fits. Apply the same combining behaviour to any similar
 expressions which have multi-line, block-indented lists of sub-expressions
-delimited by parentheses, brackets, or braces. E.g.,
+delimited by parentheses (e.g., macros or tuple struct literals). E.g.,
 
 ```rust
 foo(bar(
@@ -848,61 +848,20 @@ let arr = [combinable(
     an_expr,
     another_expr,
 )];
-
-let x = Thing(an_expr, another_expr, match cond {
-    A => 1,
-    B => 2,
-});
-
-let x = format!("Stuff: {}", [
-    an_expr,
-    another_expr,
-]);
-
-let x = func(an_expr, another_expr, SomeStruct {
-    field: this_is_long,
-    another_field: 123,
-});
 ```
 
 Apply this behavior recursively.
 
-If the last argument is a multi-line closure with an explicit block,
-only apply the combining behavior if there are no other closure arguments.
+For a function with multiple arguments, if the last argument is a multi-line
+closure with an explicit block, there are no other closure arguments, and all
+the arguments and the first line of the closure fit on the first line, use the
+same combining behavior:
 
 ```rust
-// Combinable
 foo(first_arg, x, |param| {
     action();
     foo(param)
 })
-// Not combinable, because the closure is not the last argument
-foo(
-    first_arg,
-    |param| {
-        action();
-        foo(param)
-    },
-    whatever,
-)
-// Not combinable, because the first line of the closure does not fit
-foo(
-    first_arg,
-    x,
-    move |very_long_param_causing_line_to_overflow| -> Bar {
-        action();
-        foo(param)
-    },
-)
-// Not combinable, because there is more than one closure argument
-foo(
-    first_arg,
-    |x| x.bar(),
-    |param| {
-        action();
-        foo(param)
-    },
-)
 ```
 
 ## Ranges

From 346fef4b3bfcff53beb1979103cc14b83cc9a14d Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Thu, 30 Jan 2025 18:24:02 +0000
Subject: [PATCH 292/342] Disable overflow_delimited_expr in edition 2024

---
 src/tools/rustfmt/src/bin/main.rs             |  9 +--
 src/tools/rustfmt/src/config/mod.rs           |  2 +-
 src/tools/rustfmt/src/config/options.rs       |  4 +-
 .../style_edition/overflow_delim_expr_2024.rs | 73 +++++++++++--------
 4 files changed, 51 insertions(+), 37 deletions(-)

diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs
index 34984798ae60..28df49b9304c 100644
--- a/src/tools/rustfmt/src/bin/main.rs
+++ b/src/tools/rustfmt/src/bin/main.rs
@@ -817,7 +817,6 @@ mod test {
         options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]);
         let config = get_config(None, Some(options));
         assert_eq!(config.style_edition(), StyleEdition::Edition2024);
-        assert_eq!(config.overflow_delimited_expr(), true);
     }
 
     #[nightly_only_test]
@@ -827,7 +826,6 @@ mod test {
         let config_file = Some(Path::new("tests/config/style-edition/just-version"));
         let config = get_config(config_file, Some(options));
         assert_eq!(config.style_edition(), StyleEdition::Edition2024);
-        assert_eq!(config.overflow_delimited_expr(), true);
     }
 
     #[nightly_only_test]
@@ -872,7 +870,6 @@ mod test {
         ]);
         let config = get_config(None, Some(options));
         assert_eq!(config.style_edition(), StyleEdition::Edition2024);
-        assert_eq!(config.overflow_delimited_expr(), true);
     }
 
     #[nightly_only_test]
@@ -938,7 +935,6 @@ mod test {
         options.style_edition = Some(StyleEdition::Edition2024);
         let config = get_config(None, Some(options));
         assert_eq!(config.style_edition(), StyleEdition::Edition2024);
-        assert_eq!(config.overflow_delimited_expr(), true);
     }
 
     #[nightly_only_test]
@@ -948,6 +944,8 @@ mod test {
         let config_file = Some(Path::new("tests/config/style-edition/overrides"));
         let config = get_config(config_file, Some(options));
         assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        // FIXME: this test doesn't really exercise anything, since
+        // `overflow_delimited_expr` is disabled by default in edition 2024.
         assert_eq!(config.overflow_delimited_expr(), false);
     }
 
@@ -959,7 +957,8 @@ mod test {
         options.inline_config =
             HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]);
         let config = get_config(config_file, Some(options));
-        assert_eq!(config.style_edition(), StyleEdition::Edition2024);
+        // FIXME: this test doesn't really exercise anything, since
+        // `overflow_delimited_expr` is disabled by default in edition 2024.
         assert_eq!(config.overflow_delimited_expr(), false);
     }
 }
diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs
index 7355adc9f9df..6b63108c037e 100644
--- a/src/tools/rustfmt/src/config/mod.rs
+++ b/src/tools/rustfmt/src/config/mod.rs
@@ -848,7 +848,7 @@ binop_separator = "Front"
 remove_nested_parens = true
 combine_control_expr = true
 short_array_element_width_threshold = 10
-overflow_delimited_expr = true
+overflow_delimited_expr = false
 struct_field_align_threshold = 0
 enum_discrim_align_threshold = 0
 match_arm_blocks = true
diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs
index bbc99a2dced2..71865ec75ce6 100644
--- a/src/tools/rustfmt/src/config/options.rs
+++ b/src/tools/rustfmt/src/config/options.rs
@@ -627,7 +627,7 @@ config_option_with_style_edition_default!(
     RemoveNestedParens, bool, _ => true;
     CombineControlExpr, bool, _ => true;
     ShortArrayElementWidthThreshold, usize, _ => 10;
-    OverflowDelimitedExpr, bool, Edition2024 => true, _ => false;
+    OverflowDelimitedExpr, bool, _ => false;
     StructFieldAlignThreshold, usize, _ => 0;
     EnumDiscrimAlignThreshold, usize, _ => 0;
     MatchArmBlocks, bool, _ => true;
@@ -644,7 +644,7 @@ config_option_with_style_edition_default!(
     BlankLinesLowerBound, usize, _ => 0;
     EditionConfig, Edition, _ => Edition::Edition2015;
     StyleEditionConfig, StyleEdition,
-        Edition2024 =>  StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
+        Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
     VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
     InlineAttributeWidth, usize, _ => 0;
     FormatGeneratedFiles, bool, _ => true;
diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs
index ecd2e8ca7970..1b2d12ce3201 100644
--- a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs
+++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs
@@ -25,10 +25,13 @@ fn combine_blocklike() {
         y: value2,
     });
 
-    do_thing(x, Bar {
-        x: value,
-        y: value2,
-    });
+    do_thing(
+        x,
+        Bar {
+            x: value,
+            y: value2,
+        },
+    );
 
     do_thing(
         x,
@@ -46,12 +49,15 @@ fn combine_blocklike() {
         value4_with_longer_name,
     ]);
 
-    do_thing(x, &[
-        value_with_longer_name,
-        value2_with_longer_name,
-        value3_with_longer_name,
-        value4_with_longer_name,
-    ]);
+    do_thing(
+        x,
+        &[
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
 
     do_thing(
         x,
@@ -71,12 +77,15 @@ fn combine_blocklike() {
         value4_with_longer_name,
     ]);
 
-    do_thing(x, vec![
-        value_with_longer_name,
-        value2_with_longer_name,
-        value3_with_longer_name,
-        value4_with_longer_name,
-    ]);
+    do_thing(
+        x,
+        vec![
+            value_with_longer_name,
+            value2_with_longer_name,
+            value3_with_longer_name,
+            value4_with_longer_name,
+        ],
+    );
 
     do_thing(
         x,
@@ -99,22 +108,28 @@ fn combine_blocklike() {
 }
 
 fn combine_struct_sample() {
-    let identity = verify(&ctx, VerifyLogin {
-        type_: LoginType::Username,
-        username: args.username.clone(),
-        password: Some(args.password.clone()),
-        domain: None,
-    })?;
+    let identity = verify(
+        &ctx,
+        VerifyLogin {
+            type_: LoginType::Username,
+            username: args.username.clone(),
+            password: Some(args.password.clone()),
+            domain: None,
+        },
+    )?;
 }
 
 fn combine_macro_sample() {
     rocket::ignite()
-        .mount("/", routes![
-            http::auth::login,
-            http::auth::logout,
-            http::cors::options,
-            http::action::dance,
-            http::action::sleep,
-        ])
+        .mount(
+            "/",
+            routes![
+                http::auth::login,
+                http::auth::logout,
+                http::cors::options,
+                http::action::dance,
+                http::action::sleep,
+            ],
+        )
         .launch();
 }

From d3a148fe07bf2bcab0d262463f0f892f555e0aa6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Fri, 25 Oct 2024 21:27:13 +0000
Subject: [PATCH 293/342] When encountering unexpected closure return type,
 point at return type/expression

```
error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to be a closure that returns `()`, but it returns `!`
  --> $DIR/fallback-closure-wrap.rs:19:9
   |
LL |     let error = Closure::wrap(Box::new(move || {
   |                                        -------
LL |         panic!("Can't connect to server.");
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `!`
   |
   = note: expected unit type `()`
                   found type `!`
   = note: required for the cast from `Box<{closure@$DIR/fallback-closure-wrap.rs:18:40: 18:47}>` to `Box`
```

```
error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to be a closure that returns `bool`, but it returns `Option<()>`
  --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:16
   |
LL |     call(|| -> Option<()> {
   |     ---- ------^^^^^^^^^^
   |     |          |
   |     |          expected `bool`, found `Option<()>`
   |     required by a bound introduced by this call
   |
   = note: expected type `bool`
              found enum `Option<()>`
note: required by a bound in `call`
  --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:3:25
   |
LL | fn call(_: impl Fn() -> bool) {}
   |                         ^^^^ required by this bound in `call`
```

```
error[E0271]: expected `{closure@f670.rs:28:13}` to be a closure that returns `Result<(), _>`, but it returns `!`
    --> f670.rs:28:20
     |
28   |     let c = |e| -> ! {
     |             -------^
     |                    |
     |                    expected `Result<(), _>`, found `!`
...
32   |     f().or_else(c);
     |         ------- required by a bound introduced by this call
-Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs:1433:28
     |
     = note: expected enum `Result<(), _>`
                found type `!`
note: required by a bound in `Result::::or_else`
    --> /home/gh-estebank/rust/library/core/src/result.rs:1406:39
     |
1406 |     pub fn or_else Result>(self, op: O) -> Result {
     |                                       ^^^^^^^^^^^^ required by this bound in `Result::::or_else`
```
---
 .../src/check/compare_impl_item.rs            |  3 +
 compiler/rustc_hir_analysis/src/check/mod.rs  |  1 +
 .../rustc_hir_typeck/src/fn_ctxt/checks.rs    |  1 +
 compiler/rustc_passes/src/check_attr.rs       |  1 +
 .../src/error_reporting/infer/mod.rs          |  4 +-
 .../traits/fulfillment_errors.rs              | 98 ++++++++++++++-----
 ...r-type-mismatch-in-closure-in-async.stderr | 17 ++--
 .../trait-bounds/issue-62203-hrtb-ice.rs      |  6 +-
 .../trait-bounds/issue-62203-hrtb-ice.stderr  | 33 +++----
 .../fallback-closure-wrap.fallback.stderr     | 13 ++-
 tests/ui/never_type/fallback-closure-wrap.rs  |  2 +-
 11 files changed, 114 insertions(+), 65 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 7332888c5f91..717d6f9dd737 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -642,6 +642,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
             );
             let hir = tcx.hir();
             infcx.err_ctxt().note_type_err(
+                cause.span,
                 &mut diag,
                 &cause,
                 hir.get_if_local(impl_m.def_id)
@@ -1061,6 +1062,7 @@ fn report_trait_method_mismatch<'tcx>(
 
     cause.span = impl_err_span;
     infcx.err_ctxt().note_type_err(
+        cause.span,
         &mut diag,
         &cause,
         trait_err_span.map(|sp| (sp, Cow::from("type in trait"), false)),
@@ -1853,6 +1855,7 @@ fn compare_const_predicate_entailment<'tcx>(
         });
 
         infcx.err_ctxt().note_type_err(
+            cause.span,
             &mut diag,
             &cause,
             trait_c_span.map(|span| (span, Cow::from("type in trait"), false)),
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 814c784710a1..3cf662a635af 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -640,6 +640,7 @@ pub fn check_function_signature<'tcx>(
             let failure_code = cause.as_failure_code_diag(err, cause.span, vec![]);
             let mut diag = tcx.dcx().create_err(failure_code);
             err_ctxt.note_type_err(
+                cause.span,
                 &mut diag,
                 &cause,
                 None,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 8e78fb3e2191..e76ce420bedc 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1141,6 +1141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty);
                         if let Some(e) = error {
                             self.err_ctxt().note_type_err(
+                                trace.cause.span,
                                 &mut err,
                                 &trace.cause,
                                 None,
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index e19819a22b4f..c9a429846fd8 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -2462,6 +2462,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
 
             infcx.err_ctxt().note_type_err(
+                cause.span,
                 &mut diag,
                 &cause,
                 None,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 9eacd377361a..da9386c00ba8 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1386,6 +1386,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, diag, secondary_span, prefer_label))]
     pub fn note_type_err(
         &self,
+        span: Span,
         diag: &mut Diag<'_>,
         cause: &ObligationCause<'tcx>,
         secondary_span: Option<(Span, Cow<'static, str>, bool)>,
@@ -1393,8 +1394,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         terr: TypeError<'tcx>,
         prefer_label: bool,
     ) {
-        let span = cause.span;
-
         // For some types of errors, expected-found does not make
         // sense, so just ignore the values we were given.
         if let TypeError::CyclicTy(_) = terr {
@@ -2053,6 +2052,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
         let mut diag = self.dcx().create_err(failure_code);
         self.note_type_err(
+            span,
             &mut diag,
             &trace.cause,
             None,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 6d39cbce3b78..24615dfdc56c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -702,6 +702,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 );
 
                 self.note_type_err(
+                    span,
                     &mut diag,
                     &obligation.cause,
                     None,
@@ -931,14 +932,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
         let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
-        let body_id = match self.tcx.hir_node(hir_id) {
-            hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Fn { body: body_id, .. }, ..
-            }) => body_id,
-            _ => return false,
-        };
-        let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span })
-            .visit_body(self.tcx.hir().body(*body_id))
+        let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false };
+        let ControlFlow::Break(expr) =
+            (FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir().body(body_id))
         else {
             return false;
         };
@@ -1385,9 +1381,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 _ => (None, error.err),
             };
 
-            let msg = values
+            let (msg, span, closure_span) = values
                 .and_then(|(predicate, normalized_term, expected_term)| {
-                    self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
+                    self.maybe_detailed_projection_msg(
+                        obligation.cause.span,
+                        predicate,
+                        normalized_term,
+                        expected_term,
+                    )
                 })
                 .unwrap_or_else(|| {
                     let mut cx = FmtPrinter::new_with_limit(
@@ -1395,12 +1396,35 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         Namespace::TypeNS,
                         rustc_session::Limit(10),
                     );
-                    with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
-                        self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
-                        cx.into_buffer()
-                    }))
+                    (
+                        with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
+                            self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
+                            cx.into_buffer()
+                        })),
+                        obligation.cause.span,
+                        None,
+                    )
                 });
-            let mut diag = struct_span_code_err!(self.dcx(), obligation.cause.span, E0271, "{msg}");
+            let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}");
+            if let Some(span) = closure_span {
+                // Mark the closure decl so that it is seen even if we are pointing at the return
+                // type or expression.
+                //
+                // error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns
+                //               `Unit3`, but it returns `Unit4`
+                //   --> $DIR/foo.rs:43:17
+                //    |
+                // LL |     let v = Unit2.m(
+                //    |                   - required by a bound introduced by this call
+                // ...
+                // LL |             f: |x| {
+                //    |                --- /* this span */
+                // LL |                 drop(x);
+                // LL |                 Unit4
+                //    |                 ^^^^^ expected `Unit3`, found `Unit4`
+                //    |
+                diag.span_label(span, "");
+            }
 
             let secondary_span = (|| {
                 let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
@@ -1460,6 +1484,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             })();
 
             self.note_type_err(
+                span,
                 &mut diag,
                 &obligation.cause,
                 secondary_span,
@@ -1479,34 +1504,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     fn maybe_detailed_projection_msg(
         &self,
+        mut span: Span,
         projection_term: ty::AliasTerm<'tcx>,
         normalized_ty: ty::Term<'tcx>,
         expected_ty: ty::Term<'tcx>,
-    ) -> Option {
+    ) -> Option<(String, Span, Option)> {
         let trait_def_id = projection_term.trait_def_id(self.tcx);
         let self_ty = projection_term.self_ty();
 
         with_forced_trimmed_paths! {
             if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) {
                 let fn_kind = self_ty.prefix_string(self.tcx);
+                let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() {
+                    let def_span = self.tcx.def_span(def_id);
+                    let node = self.tcx.hir_node_by_def_id(def_id.as_local().unwrap());
+                    if let Some(fn_decl) = node.fn_decl() {
+                        span = match fn_decl.output {
+                            hir::FnRetTy::Return(ty) => ty.span,
+                            hir::FnRetTy::DefaultReturn(_) => {
+                                let body = self.tcx.hir().body(node.body_id().unwrap());
+                                match body.value.kind {
+                                    hir::ExprKind::Block(
+                                        hir::Block { expr: Some(expr), .. },
+                                        _,
+                                    ) => expr.span,
+                                    hir::ExprKind::Block(
+                                        hir::Block {
+                                            expr: None, stmts: [.., last], ..
+                                        },
+                                        _,
+                                    ) => last.span,
+                                    _ => body.value.span,
+                                }
+                            }
+                        };
+                    }
+                    (span, Some(def_span))
+                } else {
+                    (span, None)
+                };
                 let item = match self_ty.kind() {
                     ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),
                     _ => self_ty.to_string(),
                 };
-                Some(format!(
+                Some((format!(
                     "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \
                      returns `{normalized_ty}`",
-                ))
+                ), span, closure_span))
             } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) {
-                Some(format!(
+                Some((format!(
                     "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \
                      resolves to `{normalized_ty}`"
-                ))
+                ), span, None))
             } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) {
-                Some(format!(
+                Some((format!(
                     "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \
                      yields `{normalized_ty}`"
-                ))
+                ), span, None))
             } else {
                 None
             }
diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr
index b60f6a083386..6e8df91767de 100644
--- a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr
+++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr
@@ -21,18 +21,13 @@ LL |         true
               found type `bool`
 
 error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to be a closure that returns `bool`, but it returns `Option<()>`
-  --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10
+  --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:16
    |
-LL |       call(|| -> Option<()> {
-   |  _____----_^
-   | |     |
-   | |     required by a bound introduced by this call
-LL | |
-LL | |         if true {
-LL | |             false
-...  |
-LL | |     })
-   | |_____^ expected `bool`, found `Option<()>`
+LL |     call(|| -> Option<()> {
+   |     ---- ------^^^^^^^^^^
+   |     |          |
+   |     |          expected `bool`, found `Option<()>`
+   |     required by a bound introduced by this call
    |
    = note: expected type `bool`
               found enum `Option<()>`
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs
index e70f6fc3430f..f2b73d692618 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs
+++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs
@@ -36,12 +36,10 @@ trait Ty<'a> {
 
 fn main() {
     let v = Unit2.m(
-        L {
-            //~^ ERROR to be a closure that returns `Unit3`, but it returns `Unit4`
-            //~| ERROR type mismatch
+        L { //~ ERROR type mismatch
             f: |x| {
                 drop(x);
-                Unit4
+                Unit4 //~ ERROR to be a closure that returns `Unit3`, but it returns `Unit4`
             },
         },
     );
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr
index 74610b55dc37..411b99e2bbba 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr
@@ -1,16 +1,15 @@
-error[E0271]: type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V`
+error[E0271]: type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V`
   --> $DIR/issue-62203-hrtb-ice.rs:39:9
    |
 LL |       let v = Unit2.m(
    |                     - required by a bound introduced by this call
 LL | /         L {
-LL | |
-LL | |
 LL | |             f: |x| {
-...  |
+LL | |                 drop(x);
+LL | |                 Unit4
 LL | |             },
 LL | |         },
-   | |_________^ type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V`
+   | |_________^ type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V`
    |
 note: expected this to be `<_ as Ty<'_>>::V`
   --> $DIR/issue-62203-hrtb-ice.rs:21:14
@@ -30,21 +29,19 @@ LL |     where
 LL |         F: for<'r> T0<'r, (>::V,), O = >::V>,
    |                                                   ^^^^^^^^^^^^^^^^^^^^ required by this bound in `T1::m`
 
-error[E0271]: expected `{closure@issue-62203-hrtb-ice.rs:42:16}` to be a closure that returns `Unit3`, but it returns `Unit4`
-  --> $DIR/issue-62203-hrtb-ice.rs:39:9
+error[E0271]: expected `{closure@issue-62203-hrtb-ice.rs:40:16}` to be a closure that returns `Unit3`, but it returns `Unit4`
+  --> $DIR/issue-62203-hrtb-ice.rs:42:17
    |
-LL |       let v = Unit2.m(
-   |                     - required by a bound introduced by this call
-LL | /         L {
-LL | |
-LL | |
-LL | |             f: |x| {
-...  |
-LL | |             },
-LL | |         },
-   | |_________^ expected `Unit3`, found `Unit4`
+LL |     let v = Unit2.m(
+   |                   - required by a bound introduced by this call
+LL |         L {
+LL |             f: |x| {
+   |                ---
+LL |                 drop(x);
+LL |                 Unit4
+   |                 ^^^^^ expected `Unit3`, found `Unit4`
    |
-note: required for `L<{closure@$DIR/issue-62203-hrtb-ice.rs:42:16: 42:19}>` to implement `for<'r> T0<'r, (&'r u8,)>`
+note: required for `L<{closure@$DIR/issue-62203-hrtb-ice.rs:40:16: 40:19}>` to implement `for<'r> T0<'r, (&'r u8,)>`
   --> $DIR/issue-62203-hrtb-ice.rs:17:16
    |
 LL | impl<'a, A, T> T0<'a, A> for L
diff --git a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr
index aa4a2760ffdc..031c522b69e5 100644
--- a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr
+++ b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr
@@ -1,16 +1,15 @@
 error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to be a closure that returns `()`, but it returns `!`
-  --> $DIR/fallback-closure-wrap.rs:18:31
+  --> $DIR/fallback-closure-wrap.rs:19:9
    |
-LL |       let error = Closure::wrap(Box::new(move || {
-   |  _______________________________^
-LL | |
-LL | |         panic!("Can't connect to server.");
-LL | |     }) as Box);
-   | |______^ expected `()`, found `!`
+LL |     let error = Closure::wrap(Box::new(move || {
+   |                                        -------
+LL |         panic!("Can't connect to server.");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `!`
    |
    = note: expected unit type `()`
                    found type `!`
    = note: required for the cast from `Box<{closure@$DIR/fallback-closure-wrap.rs:18:40: 18:47}>` to `Box`
+   = note: this error originates in the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/never_type/fallback-closure-wrap.rs b/tests/ui/never_type/fallback-closure-wrap.rs
index 27020289c683..e97505b6bc0c 100644
--- a/tests/ui/never_type/fallback-closure-wrap.rs
+++ b/tests/ui/never_type/fallback-closure-wrap.rs
@@ -16,8 +16,8 @@ use std::marker::PhantomData;
 
 fn main() {
     let error = Closure::wrap(Box::new(move || {
-        //[fallback]~^ to be a closure that returns `()`, but it returns `!`
         panic!("Can't connect to server.");
+        //[fallback]~^ to be a closure that returns `()`, but it returns `!`
     }) as Box);
 }
 

From 03e9a383906ca85e264f056f490c091fdef30a90 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Fri, 25 Oct 2024 23:23:15 +0000
Subject: [PATCH 294/342] On E0271 for a closure behind a binding, point at
 binding in call too

```
error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:18:13}` to be a closure that returns `Result<(), _>`, but it returns `!`
    --> tests/ui/closures/return-type-doesnt-match-bound.rs:18:20
     |
18   |     let c = |e| -> ! { //~ ERROR to be a closure that returns
     |             -------^
     |                    |
     |                    expected `Result<(), _>`, found `!`
...
22   |     f().or_else(c);
     |         ------- -
     |         |
     |         required by a bound introduced by this call
     |
     = note: expected enum `Result<(), _>`
                found type `!`
note: required by a bound in `Result::::or_else`
    --> /home/gh-estebank/rust/library/core/src/result.rs:1406:39
     |
1406 |     pub fn or_else Result>(self, op: O) -> Result {
     |                                       ^^^^^^^^^^^^ required by this bound in `Result::::or_else`
```
---
 .../traits/fulfillment_errors.rs              |  4 +++
 .../return-type-doesnt-match-bound.rs         | 25 +++++++++++++
 .../return-type-doesnt-match-bound.stderr     | 35 +++++++++++++++++++
 3 files changed, 64 insertions(+)
 create mode 100644 tests/ui/closures/return-type-doesnt-match-bound.rs
 create mode 100644 tests/ui/closures/return-type-doesnt-match-bound.stderr

diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 24615dfdc56c..78f6cd9e6563 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1424,6 +1424,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 //    |                 ^^^^^ expected `Unit3`, found `Unit4`
                 //    |
                 diag.span_label(span, "");
+                if !span.overlaps(obligation.cause.span) {
+                    // Point at the binding corresponding to the closure where it is used.
+                    diag.span_label(obligation.cause.span, "");
+                }
             }
 
             let secondary_span = (|| {
diff --git a/tests/ui/closures/return-type-doesnt-match-bound.rs b/tests/ui/closures/return-type-doesnt-match-bound.rs
new file mode 100644
index 000000000000..f9098d0cb5cc
--- /dev/null
+++ b/tests/ui/closures/return-type-doesnt-match-bound.rs
@@ -0,0 +1,25 @@
+use std::error::Error;
+use std::process::exit;
+
+fn foo(f: F) -> ()
+where
+    F: FnOnce() -> Result<(), Box>,
+{
+    f().or_else(|e| -> ! { //~ ERROR to be a closure that returns
+        eprintln!("{:?}", e);
+        exit(1)
+    });
+}
+
+fn bar(f: F) -> ()
+where
+    F: FnOnce() -> Result<(), Box>,
+{
+    let c = |e| -> ! { //~ ERROR to be a closure that returns
+        eprintln!("{:?}", e);
+        exit(1)
+    };
+    f().or_else(c);
+}
+
+fn main() {}
diff --git a/tests/ui/closures/return-type-doesnt-match-bound.stderr b/tests/ui/closures/return-type-doesnt-match-bound.stderr
new file mode 100644
index 000000000000..c6aad2054f54
--- /dev/null
+++ b/tests/ui/closures/return-type-doesnt-match-bound.stderr
@@ -0,0 +1,35 @@
+error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:8:17}` to be a closure that returns `Result<(), _>`, but it returns `!`
+  --> $DIR/return-type-doesnt-match-bound.rs:8:24
+   |
+LL |     f().or_else(|e| -> ! {
+   |         ------- -------^
+   |         |              |
+   |         |              expected `Result<(), _>`, found `!`
+   |         required by a bound introduced by this call
+   |
+   = note: expected enum `Result<(), _>`
+              found type `!`
+note: required by a bound in `Result::::or_else`
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+
+error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:18:13}` to be a closure that returns `Result<(), _>`, but it returns `!`
+  --> $DIR/return-type-doesnt-match-bound.rs:18:20
+   |
+LL |     let c = |e| -> ! {
+   |             -------^
+   |                    |
+   |                    expected `Result<(), _>`, found `!`
+...
+LL |     f().or_else(c);
+   |         ------- -
+   |         |
+   |         required by a bound introduced by this call
+   |
+   = note: expected enum `Result<(), _>`
+              found type `!`
+note: required by a bound in `Result::::or_else`
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0271`.

From 87d323c81e77545fc9a3878277430c1e0d58805f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Sun, 27 Oct 2024 19:03:13 +0000
Subject: [PATCH 295/342] Add closure labels

---
 .../src/error_reporting/traits/fulfillment_errors.rs |  4 ++--
 ...-ice-for-type-mismatch-in-closure-in-async.stderr |  5 +++--
 .../closures/return-type-doesnt-match-bound.stderr   | 12 +++++++-----
 .../trait-bounds/issue-62203-hrtb-ice.stderr         |  2 +-
 .../never_type/fallback-closure-wrap.fallback.stderr |  2 +-
 5 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 78f6cd9e6563..22e88b36af32 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1423,10 +1423,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 // LL |                 Unit4
                 //    |                 ^^^^^ expected `Unit3`, found `Unit4`
                 //    |
-                diag.span_label(span, "");
+                diag.span_label(span, "this closure");
                 if !span.overlaps(obligation.cause.span) {
                     // Point at the binding corresponding to the closure where it is used.
-                    diag.span_label(obligation.cause.span, "");
+                    diag.span_label(obligation.cause.span, "closure used here");
                 }
             }
 
diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr
index 6e8df91767de..f478d55d10e3 100644
--- a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr
+++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr
@@ -25,8 +25,9 @@ error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.
    |
 LL |     call(|| -> Option<()> {
    |     ---- ------^^^^^^^^^^
-   |     |          |
-   |     |          expected `bool`, found `Option<()>`
+   |     |    |     |
+   |     |    |     expected `bool`, found `Option<()>`
+   |     |    this closure
    |     required by a bound introduced by this call
    |
    = note: expected type `bool`
diff --git a/tests/ui/closures/return-type-doesnt-match-bound.stderr b/tests/ui/closures/return-type-doesnt-match-bound.stderr
index c6aad2054f54..f796a5552ac0 100644
--- a/tests/ui/closures/return-type-doesnt-match-bound.stderr
+++ b/tests/ui/closures/return-type-doesnt-match-bound.stderr
@@ -3,8 +3,9 @@ error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:8:17}` to be
    |
 LL |     f().or_else(|e| -> ! {
    |         ------- -------^
-   |         |              |
-   |         |              expected `Result<(), _>`, found `!`
+   |         |       |      |
+   |         |       |      expected `Result<(), _>`, found `!`
+   |         |       this closure
    |         required by a bound introduced by this call
    |
    = note: expected enum `Result<(), _>`
@@ -17,11 +18,12 @@ error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:18:13}` to be
    |
 LL |     let c = |e| -> ! {
    |             -------^
-   |                    |
-   |                    expected `Result<(), _>`, found `!`
+   |             |      |
+   |             |      expected `Result<(), _>`, found `!`
+   |             this closure
 ...
 LL |     f().or_else(c);
-   |         ------- -
+   |         ------- - closure used here
    |         |
    |         required by a bound introduced by this call
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr
index 411b99e2bbba..d0675d5020c8 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr
@@ -36,7 +36,7 @@ LL |     let v = Unit2.m(
    |                   - required by a bound introduced by this call
 LL |         L {
 LL |             f: |x| {
-   |                ---
+   |                --- this closure
 LL |                 drop(x);
 LL |                 Unit4
    |                 ^^^^^ expected `Unit3`, found `Unit4`
diff --git a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr
index 031c522b69e5..a559a95a334a 100644
--- a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr
+++ b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr
@@ -2,7 +2,7 @@ error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to be a closur
   --> $DIR/fallback-closure-wrap.rs:19:9
    |
 LL |     let error = Closure::wrap(Box::new(move || {
-   |                                        -------
+   |                                        ------- this closure
 LL |         panic!("Can't connect to server.");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `!`
    |

From d2a781a2ecbf02edf11deb5b3320d10ae5f34830 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Sun, 27 Oct 2024 19:12:16 +0000
Subject: [PATCH 296/342] Remove `unwrap()`s

---
 .../src/error_reporting/traits/fulfillment_errors.rs     | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 22e88b36af32..3b313530a603 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1521,12 +1521,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 let fn_kind = self_ty.prefix_string(self.tcx);
                 let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() {
                     let def_span = self.tcx.def_span(def_id);
-                    let node = self.tcx.hir_node_by_def_id(def_id.as_local().unwrap());
-                    if let Some(fn_decl) = node.fn_decl() {
+                    if let Some(local_def_id) = def_id.as_local()
+                        && let node = self.tcx.hir_node_by_def_id(local_def_id)
+                        && let Some(fn_decl) = node.fn_decl()
+                        && let Some(id) = node.body_id()
+                    {
                         span = match fn_decl.output {
                             hir::FnRetTy::Return(ty) => ty.span,
                             hir::FnRetTy::DefaultReturn(_) => {
-                                let body = self.tcx.hir().body(node.body_id().unwrap());
+                                let body = self.tcx.hir().body(id);
                                 match body.value.kind {
                                     hir::ExprKind::Block(
                                         hir::Block { expr: Some(expr), .. },

From d11676711347a220339f3601c79f74076bdb43a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Sun, 27 Oct 2024 19:23:21 +0000
Subject: [PATCH 297/342] review comment: change `span` argument

---
 .../rustc_hir_analysis/src/check/compare_impl_item.rs    | 6 +++---
 compiler/rustc_hir_analysis/src/check/mod.rs             | 2 +-
 compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs          | 2 +-
 compiler/rustc_passes/src/check_attr.rs                  | 2 +-
 .../src/error_reporting/infer/mod.rs                     | 9 +++++++--
 .../src/error_reporting/traits/fulfillment_errors.rs     | 4 ++--
 6 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 717d6f9dd737..5823d86776cd 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -642,7 +642,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
             );
             let hir = tcx.hir();
             infcx.err_ctxt().note_type_err(
-                cause.span,
                 &mut diag,
                 &cause,
                 hir.get_if_local(impl_m.def_id)
@@ -654,6 +653,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                 }))),
                 terr,
                 false,
+                None,
             );
             return Err(diag.emit());
         }
@@ -1062,7 +1062,6 @@ fn report_trait_method_mismatch<'tcx>(
 
     cause.span = impl_err_span;
     infcx.err_ctxt().note_type_err(
-        cause.span,
         &mut diag,
         &cause,
         trait_err_span.map(|sp| (sp, Cow::from("type in trait"), false)),
@@ -1072,6 +1071,7 @@ fn report_trait_method_mismatch<'tcx>(
         }))),
         terr,
         false,
+        None,
     );
 
     diag.emit()
@@ -1855,7 +1855,6 @@ fn compare_const_predicate_entailment<'tcx>(
         });
 
         infcx.err_ctxt().note_type_err(
-            cause.span,
             &mut diag,
             &cause,
             trait_c_span.map(|span| (span, Cow::from("type in trait"), false)),
@@ -1865,6 +1864,7 @@ fn compare_const_predicate_entailment<'tcx>(
             }))),
             terr,
             false,
+            None,
         );
         return Err(diag.emit());
     };
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 3cf662a635af..067edaa349a2 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -640,7 +640,6 @@ pub fn check_function_signature<'tcx>(
             let failure_code = cause.as_failure_code_diag(err, cause.span, vec![]);
             let mut diag = tcx.dcx().create_err(failure_code);
             err_ctxt.note_type_err(
-                cause.span,
                 &mut diag,
                 &cause,
                 None,
@@ -650,6 +649,7 @@ pub fn check_function_signature<'tcx>(
                 }))),
                 err,
                 false,
+                None,
             );
             return Err(diag.emit());
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index e76ce420bedc..fb5fc109ce46 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1141,13 +1141,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty);
                         if let Some(e) = error {
                             self.err_ctxt().note_type_err(
-                                trace.cause.span,
                                 &mut err,
                                 &trace.cause,
                                 None,
                                 Some(self.param_env.and(trace.values)),
                                 e,
                                 true,
+                                None,
                             );
                         }
                     }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c9a429846fd8..837da6e7724c 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -2462,7 +2462,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
 
             infcx.err_ctxt().note_type_err(
-                cause.span,
                 &mut diag,
                 &cause,
                 None,
@@ -2472,6 +2471,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 }))),
                 terr,
                 false,
+                None,
             );
             diag.emit();
             self.abort.set(true);
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index da9386c00ba8..01b26583c44b 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1386,14 +1386,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, diag, secondary_span, prefer_label))]
     pub fn note_type_err(
         &self,
-        span: Span,
         diag: &mut Diag<'_>,
         cause: &ObligationCause<'tcx>,
         secondary_span: Option<(Span, Cow<'static, str>, bool)>,
         mut values: Option>>,
         terr: TypeError<'tcx>,
         prefer_label: bool,
+        override_span: Option,
     ) {
+        // We use `override_span` when we want the error to point at a `Span` other than
+        // `cause.span`. This is used in E0271, when a closure is passed in where the return type
+        // isn't what was expected. We want to point at the closure's return type (or expression),
+        // instead of the expression where the closure is passed as call argument.
+        let span = override_span.unwrap_or(cause.span);
         // For some types of errors, expected-found does not make
         // sense, so just ignore the values we were given.
         if let TypeError::CyclicTy(_) = terr {
@@ -2052,13 +2057,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
         let mut diag = self.dcx().create_err(failure_code);
         self.note_type_err(
-            span,
             &mut diag,
             &trace.cause,
             None,
             Some(param_env.and(trace.values)),
             terr,
             false,
+            None,
         );
         diag
     }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 3b313530a603..6416c539f261 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -702,13 +702,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 );
 
                 self.note_type_err(
-                    span,
                     &mut diag,
                     &obligation.cause,
                     None,
                     None,
                     TypeError::Sorts(ty::error::ExpectedFound::new(expected_ty, ct_ty)),
                     false,
+                    None,
                 );
                 diag
             }
@@ -1488,7 +1488,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             })();
 
             self.note_type_err(
-                span,
                 &mut diag,
                 &obligation.cause,
                 secondary_span,
@@ -1500,6 +1499,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 }),
                 err,
                 false,
+                Some(span),
             );
             self.note_obligation_cause(&mut diag, obligation);
             diag.emit()

From c64038a51fbf7554fef717360b98454548adb908 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Thu, 30 Jan 2025 18:44:09 +0000
Subject: [PATCH 298/342] Use proper type when applying deref adjustment in
 const

---
 .../rustc_hir_typeck/src/fn_ctxt/_impl.rs     |  6 +++-
 tests/crashes/135210.rs                       |  8 ------
 .../const-traits/enforce-deref-on-adjust.rs   | 28 +++++++++++++++++++
 3 files changed, 33 insertions(+), 9 deletions(-)
 delete mode 100644 tests/crashes/135210.rs
 create mode 100644 tests/ui/traits/const-traits/enforce-deref-on-adjust.rs

diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 8e647ad3c6a4..1035d983b716 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -253,6 +253,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
 
+        let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
+
         for a in &adj {
             match a.kind {
                 Adjust::NeverToAny => {
@@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         None,
                         expr.span,
                         overloaded_deref.method_call(self.tcx),
-                        self.tcx.mk_args(&[a.target.into()]),
+                        self.tcx.mk_args(&[expr_ty.into()]),
                     );
                 }
                 Adjust::Deref(None) => {
@@ -283,6 +285,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // No effects to enforce here.
                 }
             }
+
+            expr_ty = a.target;
         }
 
         let autoborrow_mut = adj.iter().any(|adj| {
diff --git a/tests/crashes/135210.rs b/tests/crashes/135210.rs
deleted file mode 100644
index acb61e21090d..000000000000
--- a/tests/crashes/135210.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #135210
-
-#![feature(const_trait_impl)]
-const _: fn(&String) = |s| {
-    &*s as &str;
-};
-
-fn main() {}
diff --git a/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs
new file mode 100644
index 000000000000..d5240b7e18dd
--- /dev/null
+++ b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs
@@ -0,0 +1,28 @@
+//@ check-pass
+
+#![feature(const_deref)]
+#![feature(const_trait_impl)]
+
+use std::ops::Deref;
+
+struct Wrap(T);
+struct Foo;
+
+impl Foo {
+    const fn call(&self) {}
+}
+
+impl const Deref for Wrap {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+const fn foo() {
+    let x = Wrap(Foo);
+    x.call();
+}
+
+fn main() {}

From 88d7ea36e9eb0e157d465e75ace7b0a19f154b01 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Thu, 30 Jan 2025 18:39:08 +0000
Subject: [PATCH 299/342] Filter out RPITITs when suggesting unconstrained
 assoc type on too many generics

---
 .../src/errors/wrong_number_of_generic_args.rs  |  1 +
 .../dont-consider-unconstrained-rpitits.rs      | 14 ++++++++++++++
 .../dont-consider-unconstrained-rpitits.stderr  | 17 +++++++++++++++++
 3 files changed, 32 insertions(+)
 create mode 100644 tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs
 create mode 100644 tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr

diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
index 674073497294..b2501d647a55 100644
--- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
@@ -495,6 +495,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
                         .iter()
                         .any(|constraint| constraint.ident.name == item.name)
                 })
+                .filter(|item| !item.is_impl_trait_in_trait())
                 .map(|item| self.tcx.item_ident(item.def_id).to_string())
                 .collect()
         } else {
diff --git a/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs
new file mode 100644
index 000000000000..fe73306966e6
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs
@@ -0,0 +1,14 @@
+// There's a suggestion that turns `Iterator` into `Iterator`
+// if we have more generics than the trait wants. Let's not consider RPITITs
+// for this, since that makes no sense right now.
+
+trait Foo {
+    fn bar(self) -> impl Sized;
+}
+
+impl Foo for () {
+    //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied
+    fn bar(self) -> impl Sized {}
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr
new file mode 100644
index 000000000000..fb497b89c5f7
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr
@@ -0,0 +1,17 @@
+error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/dont-consider-unconstrained-rpitits.rs:9:6
+   |
+LL | impl Foo for () {
+   |      ^^^---- help: remove the unnecessary generics
+   |      |
+   |      expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/dont-consider-unconstrained-rpitits.rs:5:7
+   |
+LL | trait Foo {
+   |       ^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0107`.

From a1a55a2e0abccb31d61ecd32b197a6f8ff287e51 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 30 Jan 2025 12:05:31 -0700
Subject: [PATCH 300/342] Give 104145, 103463, and 31948 more descriptive names

---
 .../{ice-104145.rs => ice-extern-trait-local-impl-104145.rs}      | 0
 .../intra-doc/{ice-103463.rs => ice-priv-use-103463.rs}           | 0
 ...en-module-impl-31948-1.rs => doc-reachability-impl-31948-1.rs} | 0
 ...en-module-impl-31948-2.rs => doc-reachability-impl-31948-2.rs} | 0
 ...hidden-module-impl-31948.rs => doc-reachability-impl-31948.rs} | 0
 5 files changed, 0 insertions(+), 0 deletions(-)
 rename tests/rustdoc-ui/intra-doc/{ice-104145.rs => ice-extern-trait-local-impl-104145.rs} (100%)
 rename tests/rustdoc-ui/intra-doc/{ice-103463.rs => ice-priv-use-103463.rs} (100%)
 rename tests/rustdoc/inline_cross/{doc-hidden-module-impl-31948-1.rs => doc-reachability-impl-31948-1.rs} (100%)
 rename tests/rustdoc/inline_cross/{doc-hidden-module-impl-31948-2.rs => doc-reachability-impl-31948-2.rs} (100%)
 rename tests/rustdoc/inline_cross/{doc-hidden-module-impl-31948.rs => doc-reachability-impl-31948.rs} (100%)

diff --git a/tests/rustdoc-ui/intra-doc/ice-104145.rs b/tests/rustdoc-ui/intra-doc/ice-extern-trait-local-impl-104145.rs
similarity index 100%
rename from tests/rustdoc-ui/intra-doc/ice-104145.rs
rename to tests/rustdoc-ui/intra-doc/ice-extern-trait-local-impl-104145.rs
diff --git a/tests/rustdoc-ui/intra-doc/ice-103463.rs b/tests/rustdoc-ui/intra-doc/ice-priv-use-103463.rs
similarity index 100%
rename from tests/rustdoc-ui/intra-doc/ice-103463.rs
rename to tests/rustdoc-ui/intra-doc/ice-priv-use-103463.rs
diff --git a/tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-1.rs b/tests/rustdoc/inline_cross/doc-reachability-impl-31948-1.rs
similarity index 100%
rename from tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-1.rs
rename to tests/rustdoc/inline_cross/doc-reachability-impl-31948-1.rs
diff --git a/tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-2.rs b/tests/rustdoc/inline_cross/doc-reachability-impl-31948-2.rs
similarity index 100%
rename from tests/rustdoc/inline_cross/doc-hidden-module-impl-31948-2.rs
rename to tests/rustdoc/inline_cross/doc-reachability-impl-31948-2.rs
diff --git a/tests/rustdoc/inline_cross/doc-hidden-module-impl-31948.rs b/tests/rustdoc/inline_cross/doc-reachability-impl-31948.rs
similarity index 100%
rename from tests/rustdoc/inline_cross/doc-hidden-module-impl-31948.rs
rename to tests/rustdoc/inline_cross/doc-reachability-impl-31948.rs

From c17d5689dac5f82464ca09bbcd56a1c694effd92 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 30 Jan 2025 12:09:33 -0700
Subject: [PATCH 301/342] Direct link 108459 to issues -> pull redirect

---
 .../intra-doc/link-same-name-different-disambiguator-108459.rs  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs b/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs
index 0328e6435a53..0b339eaf6b27 100644
--- a/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs
+++ b/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs
@@ -1,4 +1,4 @@
-// https://github.com/rust-lang/rust/issues/108459
+// https://github.com/rust-lang/rust/pull/108459
 #![crate_name="foobar"]
 
 #![deny(rustdoc::broken_intra_doc_links)]

From 97d1b0cbcdd133c23271d68f25d1d22c65a055c0 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 14:27:02 +1100
Subject: [PATCH 302/342] Clarify a comment.

I was confused here for a bit.
---
 compiler/rustc_hir_analysis/src/check/intrinsic.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 8aa95d1c1d51..cf3d48973042 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -199,7 +199,8 @@ pub fn check_intrinsic_type(
         let split: Vec<&str> = name_str.split('_').collect();
         assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format");
 
-        //We only care about the operation here
+        // Each atomic op has variants with different suffixes (`_seq_cst`, `_acquire`, etc.). Use
+        // string ops to strip the suffixes, because the variants all get the same treatment here.
         let (n_tps, inputs, output) = match split[1] {
             "cxchg" | "cxchgweak" => (
                 1,

From b546334f6c0e5fadcd05f21bf449f90512854990 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 15:07:02 +1100
Subject: [PATCH 303/342] Avoid a duplicated error case in `fn_sig_suggestion`.

---
 compiler/rustc_hir_analysis/src/check/mod.rs | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 814c784710a1..5723e89236b3 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -455,18 +455,14 @@ fn fn_sig_suggestion<'tcx>(
     let mut output = sig.output();
 
     let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
-        output = if let ty::Alias(_, alias_ty) = *output.kind() {
-            tcx.explicit_item_self_bounds(alias_ty.def_id)
+        output = if let ty::Alias(_, alias_ty) = *output.kind()
+            && let Some(output) = tcx
+                .explicit_item_self_bounds(alias_ty.def_id)
                 .iter_instantiated_copied(tcx, alias_ty.args)
                 .find_map(|(bound, _)| {
                     bound.as_projection_clause()?.no_bound_vars()?.term.as_type()
-                })
-                .unwrap_or_else(|| {
-                    span_bug!(
-                        ident.span,
-                        "expected async fn to have `impl Future` output, but it returns {output}"
-                    )
-                })
+                }) {
+            output
         } else {
             span_bug!(
                 ident.span,

From f453f930abb5922df7b0a27fe1742bfbf3828fb3 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 16:03:46 +1100
Subject: [PATCH 304/342] Merge two `match` arms that are identical.

Also rewrite the merged arm slightly to more closely match the arm above
it.
---
 .../rustc_hir_analysis/src/coherence/builtin.rs | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 6bbd76633da8..30f51fe17246 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -404,17 +404,12 @@ pub(crate) fn coerce_unsized_info<'tcx>(
             check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty))
         }
 
-        (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl(
-            ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a },
-            ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b },
-            &|ty| Ty::new_imm_ptr(tcx, ty),
-        ),
-
-        (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl(
-            ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a },
-            ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b },
-            &|ty| Ty::new_imm_ptr(tcx, ty),
-        ),
+        (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b))
+        | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => {
+            let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
+            let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
+            check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty))
+        }
 
         (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b))
             if def_a.is_struct() && def_b.is_struct() =>

From 2dbe9fd8a638c0440fcb140efd04383d9660a4eb Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 21:27:17 +1100
Subject: [PATCH 305/342] Remove an out-of-date `FIXME` comment.

This comment made sense when this crate was called `rustc_typeck`, but
makes less sense now that it's called `rustc_hir_analysis`. Especially
given that `check_drop_impl` is only called within the crate.
---
 compiler/rustc_hir_analysis/src/check/dropck.rs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index 0876bb828485..d7dfe482da44 100644
--- a/compiler/rustc_hir_analysis/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -1,7 +1,3 @@
-// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
-//
-// We don't do any drop checking during hir typeck.
-
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::codes::*;
 use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
@@ -32,7 +28,10 @@ use crate::hir::def_id::{DefId, LocalDefId};
 ///    struct/enum definition for the nominal type itself (i.e.
 ///    cannot do `struct S; impl Drop for S { ... }`).
 ///
-pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed> {
+pub(crate) fn check_drop_impl(
+    tcx: TyCtxt<'_>,
+    drop_impl_did: DefId,
+) -> Result<(), ErrorGuaranteed> {
     match tcx.impl_polarity(drop_impl_did) {
         ty::ImplPolarity::Positive => {}
         ty::ImplPolarity::Negative => {

From cab629fbd29eab3a9d345764b28a8e0dc66bc34b Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 09:59:59 +1100
Subject: [PATCH 306/342] Merge two identical match arms.

Note: `inherit_predicates_for_delegation_item` already has these cases
merged.
---
 compiler/rustc_hir_analysis/src/delegation.rs | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs
index e65420ea8bf4..984597802a46 100644
--- a/compiler/rustc_hir_analysis/src/delegation.rs
+++ b/compiler/rustc_hir_analysis/src/delegation.rs
@@ -393,13 +393,8 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
         }
 
         (FnKind::AssocInherentImpl, FnKind::AssocTrait)
-        | (FnKind::AssocTrait, FnKind::AssocTrait) => {
-            builder
-            .with_parent(tcx.parent(def_id.into()))
-            .build()
-        }
-
-        (FnKind::AssocInherentImpl, FnKind::Free)
+        | (FnKind::AssocTrait, FnKind::AssocTrait)
+        | (FnKind::AssocInherentImpl, FnKind::Free)
         | (FnKind::AssocTrait, FnKind::Free) => {
             builder
             .with_parent(tcx.parent(def_id.into()))

From 7be593e379e4d5737700b1a09887992caea7cf87 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 11:17:06 +1100
Subject: [PATCH 307/342] Format `delegation.rs` better.

There is a comment `Delegation to inherent methods is not yet
supported.` that appears three times mid-pattern and somehow inhibits
rustfmt from formatting the enclosing `match` statement. This commit
moves them to the top of the pattern, which enables more formatting.
---
 compiler/rustc_hir_analysis/src/delegation.rs | 37 ++++++++-----------
 1 file changed, 15 insertions(+), 22 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs
index 984597802a46..fad15d2152f3 100644
--- a/compiler/rustc_hir_analysis/src/delegation.rs
+++ b/compiler/rustc_hir_analysis/src/delegation.rs
@@ -335,7 +335,8 @@ fn create_generic_args<'tcx>(
                 tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args;
 
             let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
-            let method_args = tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count));
+            let method_args =
+                tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count));
             let method_args = builder.build_from_args(method_args);
 
             tcx.mk_args_from_iter(parent_args.iter().chain(method_args))
@@ -354,9 +355,9 @@ fn create_generic_args<'tcx>(
         }
 
         // For trait impl's `sig_id` is always equal to the corresponding trait method.
+        // For inherent methods delegation is not yet supported.
         (FnKind::AssocTraitImpl, _)
         | (_, FnKind::AssocTraitImpl)
-        // Delegation to inherent methods is not yet supported.
         | (_, FnKind::AssocInherentImpl) => unreachable!(),
     }
 }
@@ -382,29 +383,26 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
     let caller_kind = fn_kind(tcx, def_id.into());
     let callee_kind = fn_kind(tcx, sig_id);
     match (caller_kind, callee_kind) {
-        (FnKind::Free, FnKind::Free)
-        | (FnKind::Free, FnKind::AssocTrait) => builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build(),
+        (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => {
+            builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build()
+        }
 
-        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
-            builder
+        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => builder
             .with_parent(tcx.parent(def_id.into()))
             .with_inheritance_kind(InheritanceKind::Own)
-            .build()
-        }
+            .build(),
 
         (FnKind::AssocInherentImpl, FnKind::AssocTrait)
         | (FnKind::AssocTrait, FnKind::AssocTrait)
         | (FnKind::AssocInherentImpl, FnKind::Free)
         | (FnKind::AssocTrait, FnKind::Free) => {
-            builder
-            .with_parent(tcx.parent(def_id.into()))
-            .build()
+            builder.with_parent(tcx.parent(def_id.into())).build()
         }
 
         // For trait impl's `sig_id` is always equal to the corresponding trait method.
+        // For inherent methods delegation is not yet supported.
         (FnKind::AssocTraitImpl, _)
         | (_, FnKind::AssocTraitImpl)
-        // Delegation to inherent methods is not yet supported.
         | (_, FnKind::AssocInherentImpl) => unreachable!(),
     }
 }
@@ -420,31 +418,26 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
     let caller_kind = fn_kind(tcx, def_id.into());
     let callee_kind = fn_kind(tcx, sig_id);
     match (caller_kind, callee_kind) {
-        (FnKind::Free, FnKind::Free)
-        | (FnKind::Free, FnKind::AssocTrait) => {
+        (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => {
             builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build()
         }
 
-        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
-            builder
+        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => builder
             .with_parent(tcx.parent(def_id.into()))
             .with_inheritance_kind(InheritanceKind::Own)
-            .build()
-        }
+            .build(),
 
         (FnKind::AssocInherentImpl, FnKind::AssocTrait)
         | (FnKind::AssocTrait, FnKind::AssocTrait)
         | (FnKind::AssocInherentImpl, FnKind::Free)
         | (FnKind::AssocTrait, FnKind::Free) => {
-            builder
-                .with_parent(tcx.parent(def_id.into()))
-                .build()
+            builder.with_parent(tcx.parent(def_id.into())).build()
         }
 
         // For trait impl's `sig_id` is always equal to the corresponding trait method.
+        // For inherent methods delegation is not yet supported.
         (FnKind::AssocTraitImpl, _)
         | (_, FnKind::AssocTraitImpl)
-        // Delegation to inherent methods is not yet supported.
         | (_, FnKind::AssocInherentImpl) => unreachable!(),
     }
 }

From 969d9336ca934eaba0dc58b20d5d3ddf8d825c28 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 11:19:17 +1100
Subject: [PATCH 308/342] Remove unnecessary builders.

`delegation.rs` has three builders: `GenericsBuilder`,
`PredicatesBuilder`, and `GenericArgsBuilder`. The first two builders
have just two optional parameters, and the third one has zero. Each
builder is used within a single function. The code is over-engineered.

This commit removes the builders, replacing each with with a single
`build_*` function. This makes the code shorter and simpler.
---
 compiler/rustc_hir_analysis/src/delegation.rs | 330 ++++++++----------
 1 file changed, 138 insertions(+), 192 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs
index fad15d2152f3..ed45833b614f 100644
--- a/compiler/rustc_hir_analysis/src/delegation.rs
+++ b/compiler/rustc_hir_analysis/src/delegation.rs
@@ -100,213 +100,156 @@ enum InheritanceKind {
     Own,
 }
 
-struct GenericsBuilder<'tcx> {
+fn build_generics<'tcx>(
     tcx: TyCtxt<'tcx>,
     sig_id: DefId,
     parent: Option,
     inh_kind: InheritanceKind,
-}
+) -> ty::Generics {
+    let mut own_params = vec![];
 
-impl<'tcx> GenericsBuilder<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, sig_id: DefId) -> GenericsBuilder<'tcx> {
-        GenericsBuilder { tcx, sig_id, parent: None, inh_kind: InheritanceKind::WithParent(false) }
+    let sig_generics = tcx.generics_of(sig_id);
+    if let InheritanceKind::WithParent(has_self) = inh_kind
+        && let Some(parent_def_id) = sig_generics.parent
+    {
+        let sig_parent_generics = tcx.generics_of(parent_def_id);
+        own_params.append(&mut sig_parent_generics.own_params.clone());
+        if !has_self {
+            own_params.remove(0);
+        }
     }
+    own_params.append(&mut sig_generics.own_params.clone());
 
-    fn with_parent(mut self, parent: DefId) -> Self {
-        self.parent = Some(parent);
-        self
-    }
+    // Lifetime parameters must be declared before type and const parameters.
+    // Therefore, When delegating from a free function to a associated function,
+    // generic parameters need to be reordered:
+    //
+    // trait Trait<'a, A> {
+    //     fn foo<'b, B>(...) {...}
+    // }
+    //
+    // reuse Trait::foo;
+    // desugaring:
+    // fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
+    //     Trait::foo(...)
+    // }
+    own_params.sort_by_key(|key| key.kind.is_ty_or_const());
 
-    fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self {
-        self.inh_kind = inh_kind;
-        self
-    }
+    let param_def_id_to_index =
+        own_params.iter().map(|param| (param.def_id, param.index)).collect();
 
-    fn build(self) -> ty::Generics {
-        let mut own_params = vec![];
+    let (parent_count, has_self) = if let Some(def_id) = parent {
+        let parent_generics = tcx.generics_of(def_id);
+        let parent_kind = tcx.def_kind(def_id);
+        (parent_generics.count(), parent_kind == DefKind::Trait)
+    } else {
+        (0, false)
+    };
 
-        let sig_generics = self.tcx.generics_of(self.sig_id);
-        if let InheritanceKind::WithParent(has_self) = self.inh_kind
-            && let Some(parent_def_id) = sig_generics.parent
+    for (idx, param) in own_params.iter_mut().enumerate() {
+        param.index = (idx + parent_count) as u32;
+        // FIXME(fn_delegation): Default parameters are not inherited, because they are
+        // not permitted in functions. Therefore, there are 2 options here:
+        //
+        // - We can create non-default generic parameters.
+        // - We can substitute default parameters into the signature.
+        //
+        // At the moment, first option has been selected as the most general.
+        if let ty::GenericParamDefKind::Type { has_default, .. }
+        | ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
         {
-            let sig_parent_generics = self.tcx.generics_of(parent_def_id);
-            own_params.append(&mut sig_parent_generics.own_params.clone());
-            if !has_self {
-                own_params.remove(0);
-            }
+            *has_default = false;
         }
-        own_params.append(&mut sig_generics.own_params.clone());
+    }
 
-        // Lifetime parameters must be declared before type and const parameters.
-        // Therefore, When delegating from a free function to a associated function,
-        // generic parameters need to be reordered:
-        //
-        // trait Trait<'a, A> {
-        //     fn foo<'b, B>(...) {...}
-        // }
-        //
-        // reuse Trait::foo;
-        // desugaring:
-        // fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) {
-        //     Trait::foo(...)
-        // }
-        own_params.sort_by_key(|key| key.kind.is_ty_or_const());
-
-        let param_def_id_to_index =
-            own_params.iter().map(|param| (param.def_id, param.index)).collect();
-
-        let (parent_count, has_self) = if let Some(def_id) = self.parent {
-            let parent_generics = self.tcx.generics_of(def_id);
-            let parent_kind = self.tcx.def_kind(def_id);
-            (parent_generics.count(), parent_kind == DefKind::Trait)
-        } else {
-            (0, false)
-        };
-
-        for (idx, param) in own_params.iter_mut().enumerate() {
-            param.index = (idx + parent_count) as u32;
-            // FIXME(fn_delegation): Default parameters are not inherited, because they are
-            // not permitted in functions. Therefore, there are 2 options here:
-            //
-            // - We can create non-default generic parameters.
-            // - We can substitute default parameters into the signature.
-            //
-            // At the moment, first option has been selected as the most general.
-            if let ty::GenericParamDefKind::Type { has_default, .. }
-            | ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind
-            {
-                *has_default = false;
-            }
-        }
-
-        ty::Generics {
-            parent: self.parent,
-            parent_count,
-            own_params,
-            param_def_id_to_index,
-            has_self,
-            has_late_bound_regions: sig_generics.has_late_bound_regions,
-        }
+    ty::Generics {
+        parent,
+        parent_count,
+        own_params,
+        param_def_id_to_index,
+        has_self,
+        has_late_bound_regions: sig_generics.has_late_bound_regions,
     }
 }
 
-struct PredicatesBuilder<'tcx> {
+fn build_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
     sig_id: DefId,
     parent: Option,
     inh_kind: InheritanceKind,
     args: ty::GenericArgsRef<'tcx>,
-}
-
-impl<'tcx> PredicatesBuilder<'tcx> {
-    fn new(
+) -> ty::GenericPredicates<'tcx> {
+    struct PredicatesCollector<'tcx> {
         tcx: TyCtxt<'tcx>,
+        preds: Vec<(ty::Clause<'tcx>, Span)>,
         args: ty::GenericArgsRef<'tcx>,
-        sig_id: DefId,
-    ) -> PredicatesBuilder<'tcx> {
-        PredicatesBuilder {
-            tcx,
-            sig_id,
-            parent: None,
-            inh_kind: InheritanceKind::WithParent(false),
-            args,
-        }
     }
 
-    fn with_parent(mut self, parent: DefId) -> Self {
-        self.parent = Some(parent);
-        self
-    }
-
-    fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self {
-        self.inh_kind = inh_kind;
-        self
-    }
-
-    fn build(self) -> ty::GenericPredicates<'tcx> {
-        struct PredicatesCollector<'tcx> {
-            tcx: TyCtxt<'tcx>,
-            preds: Vec<(ty::Clause<'tcx>, Span)>,
-            args: ty::GenericArgsRef<'tcx>,
+    impl<'tcx> PredicatesCollector<'tcx> {
+        fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> {
+            PredicatesCollector { tcx, preds: vec![], args }
         }
 
-        impl<'tcx> PredicatesCollector<'tcx> {
-            fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> {
-                PredicatesCollector { tcx, preds: vec![], args }
-            }
-
-            fn with_own_preds(
-                mut self,
-                f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>,
-                def_id: DefId,
-            ) -> Self {
-                let preds = f(def_id).instantiate_own(self.tcx, self.args);
-                self.preds.extend(preds);
-                self
-            }
-
-            fn with_preds(
-                mut self,
-                f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy,
-                def_id: DefId,
-            ) -> Self {
-                let preds = f(def_id);
-                if let Some(parent_def_id) = preds.parent {
-                    self = self.with_own_preds(f, parent_def_id);
-                }
-                self.with_own_preds(f, def_id)
-            }
+        fn with_own_preds(
+            mut self,
+            f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>,
+            def_id: DefId,
+        ) -> Self {
+            let preds = f(def_id).instantiate_own(self.tcx, self.args);
+            self.preds.extend(preds);
+            self
         }
-        let collector = PredicatesCollector::new(self.tcx, self.args);
 
-        // `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate.
-        // Note: `predicates_of` query can also add inferred outlives predicates, but that
-        // is not the case here as `sig_id` is either a trait or a function.
-        let preds = match self.inh_kind {
-            InheritanceKind::WithParent(false) => {
-                collector.with_preds(|def_id| self.tcx.explicit_predicates_of(def_id), self.sig_id)
+        fn with_preds(
+            mut self,
+            f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy,
+            def_id: DefId,
+        ) -> Self {
+            let preds = f(def_id);
+            if let Some(parent_def_id) = preds.parent {
+                self = self.with_own_preds(f, parent_def_id);
             }
-            InheritanceKind::WithParent(true) => {
-                collector.with_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id)
-            }
-            InheritanceKind::Own => {
-                collector.with_own_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id)
-            }
-        }
-        .preds;
-
-        ty::GenericPredicates {
-            parent: self.parent,
-            predicates: self.tcx.arena.alloc_from_iter(preds),
+            self.with_own_preds(f, def_id)
         }
     }
+    let collector = PredicatesCollector::new(tcx, args);
+
+    // `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate.
+    // Note: `predicates_of` query can also add inferred outlives predicates, but that
+    // is not the case here as `sig_id` is either a trait or a function.
+    let preds = match inh_kind {
+        InheritanceKind::WithParent(false) => {
+            collector.with_preds(|def_id| tcx.explicit_predicates_of(def_id), sig_id)
+        }
+        InheritanceKind::WithParent(true) => {
+            collector.with_preds(|def_id| tcx.predicates_of(def_id), sig_id)
+        }
+        InheritanceKind::Own => {
+            collector.with_own_preds(|def_id| tcx.predicates_of(def_id), sig_id)
+        }
+    }
+    .preds;
+
+    ty::GenericPredicates { parent, predicates: tcx.arena.alloc_from_iter(preds) }
 }
 
-struct GenericArgsBuilder<'tcx> {
+fn build_generic_args<'tcx>(
     tcx: TyCtxt<'tcx>,
-    remap_table: RemapTable,
     sig_id: DefId,
     def_id: LocalDefId,
-}
+    args: ty::GenericArgsRef<'tcx>,
+) -> ty::GenericArgsRef<'tcx> {
+    let caller_generics = tcx.generics_of(def_id);
+    let callee_generics = tcx.generics_of(sig_id);
 
-impl<'tcx> GenericArgsBuilder<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, sig_id: DefId, def_id: LocalDefId) -> GenericArgsBuilder<'tcx> {
-        GenericArgsBuilder { tcx, remap_table: FxHashMap::default(), sig_id, def_id }
+    let mut remap_table = FxHashMap::default();
+    for caller_param in &caller_generics.own_params {
+        let callee_index = callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap();
+        remap_table.insert(callee_index, caller_param.index);
     }
 
-    fn build_from_args(mut self, args: ty::GenericArgsRef<'tcx>) -> ty::GenericArgsRef<'tcx> {
-        let caller_generics = self.tcx.generics_of(self.def_id);
-        let callee_generics = self.tcx.generics_of(self.sig_id);
-
-        for caller_param in &caller_generics.own_params {
-            let callee_index =
-                callee_generics.param_def_id_to_index(self.tcx, caller_param.def_id).unwrap();
-            self.remap_table.insert(callee_index, caller_param.index);
-        }
-
-        let mut folder = ParamIndexRemapper { tcx: self.tcx, remap_table: self.remap_table };
-        args.fold_with(&mut folder)
-    }
+    let mut folder = ParamIndexRemapper { tcx, remap_table };
+    args.fold_with(&mut folder)
 }
 
 fn create_generic_args<'tcx>(
@@ -314,8 +257,6 @@ fn create_generic_args<'tcx>(
     def_id: LocalDefId,
     sig_id: DefId,
 ) -> ty::GenericArgsRef<'tcx> {
-    let builder = GenericArgsBuilder::new(tcx, sig_id, def_id);
-
     let caller_kind = fn_kind(tcx, def_id.into());
     let callee_kind = fn_kind(tcx, sig_id);
     match (caller_kind, callee_kind) {
@@ -325,7 +266,7 @@ fn create_generic_args<'tcx>(
         | (FnKind::AssocTrait, FnKind::Free)
         | (FnKind::AssocTrait, FnKind::AssocTrait) => {
             let args = ty::GenericArgs::identity_for_item(tcx, sig_id);
-            builder.build_from_args(args)
+            build_generic_args(tcx, sig_id, def_id, args)
         }
 
         (FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
@@ -337,7 +278,7 @@ fn create_generic_args<'tcx>(
             let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
             let method_args =
                 tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count));
-            let method_args = builder.build_from_args(method_args);
+            let method_args = build_generic_args(tcx, sig_id, def_id, method_args);
 
             tcx.mk_args_from_iter(parent_args.iter().chain(method_args))
         }
@@ -348,7 +289,7 @@ fn create_generic_args<'tcx>(
             let generic_self_ty = ty::GenericArg::from(self_ty);
 
             let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id);
-            let trait_args = builder.build_from_args(trait_args);
+            let trait_args = build_generic_args(tcx, sig_id, def_id, trait_args);
 
             let args = std::iter::once(generic_self_ty).chain(trait_args.iter().skip(1));
             tcx.mk_args_from_iter(args)
@@ -378,26 +319,26 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>(
     def_id: LocalDefId,
     sig_id: DefId,
 ) -> ty::Generics {
-    let builder = GenericsBuilder::new(tcx, sig_id);
-
     let caller_kind = fn_kind(tcx, def_id.into());
     let callee_kind = fn_kind(tcx, sig_id);
     match (caller_kind, callee_kind) {
         (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => {
-            builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build()
+            build_generics(tcx, sig_id, None, InheritanceKind::WithParent(true))
         }
 
-        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => builder
-            .with_parent(tcx.parent(def_id.into()))
-            .with_inheritance_kind(InheritanceKind::Own)
-            .build(),
+        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => {
+            build_generics(tcx, sig_id, Some(tcx.parent(def_id.into())), InheritanceKind::Own)
+        }
 
         (FnKind::AssocInherentImpl, FnKind::AssocTrait)
         | (FnKind::AssocTrait, FnKind::AssocTrait)
         | (FnKind::AssocInherentImpl, FnKind::Free)
-        | (FnKind::AssocTrait, FnKind::Free) => {
-            builder.with_parent(tcx.parent(def_id.into())).build()
-        }
+        | (FnKind::AssocTrait, FnKind::Free) => build_generics(
+            tcx,
+            sig_id,
+            Some(tcx.parent(def_id.into())),
+            InheritanceKind::WithParent(false),
+        ),
 
         // For trait impl's `sig_id` is always equal to the corresponding trait method.
         // For inherent methods delegation is not yet supported.
@@ -413,26 +354,31 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>(
     sig_id: DefId,
 ) -> ty::GenericPredicates<'tcx> {
     let args = create_generic_args(tcx, def_id, sig_id);
-    let builder = PredicatesBuilder::new(tcx, args, sig_id);
-
     let caller_kind = fn_kind(tcx, def_id.into());
     let callee_kind = fn_kind(tcx, sig_id);
     match (caller_kind, callee_kind) {
         (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => {
-            builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build()
+            build_predicates(tcx, sig_id, None, InheritanceKind::WithParent(true), args)
         }
 
-        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => builder
-            .with_parent(tcx.parent(def_id.into()))
-            .with_inheritance_kind(InheritanceKind::Own)
-            .build(),
+        (FnKind::AssocTraitImpl, FnKind::AssocTrait) => build_predicates(
+            tcx,
+            sig_id,
+            Some(tcx.parent(def_id.into())),
+            InheritanceKind::Own,
+            args,
+        ),
 
         (FnKind::AssocInherentImpl, FnKind::AssocTrait)
         | (FnKind::AssocTrait, FnKind::AssocTrait)
         | (FnKind::AssocInherentImpl, FnKind::Free)
-        | (FnKind::AssocTrait, FnKind::Free) => {
-            builder.with_parent(tcx.parent(def_id.into())).build()
-        }
+        | (FnKind::AssocTrait, FnKind::Free) => build_predicates(
+            tcx,
+            sig_id,
+            Some(tcx.parent(def_id.into())),
+            InheritanceKind::WithParent(false),
+            args,
+        ),
 
         // For trait impl's `sig_id` is always equal to the corresponding trait method.
         // For inherent methods delegation is not yet supported.

From c2e37d32d54f8dd348f1f6cb69350eb4db7050ff Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 12:50:20 +1100
Subject: [PATCH 309/342] Remove an unused arg from the trait method
 `provided_kind`.

---
 compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs | 2 +-
 compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs      | 2 --
 compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs              | 1 -
 compiler/rustc_hir_typeck/src/method/confirm.rs             | 1 -
 4 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index fe3dcb35639f..431373978701 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -273,7 +273,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
 
                             // We lower to an infer even when the feature gate is not enabled
                             // as it is useful for diagnostics to be able to see a `ConstKind::Infer`
-                            args.push(ctx.provided_kind(&args, param, arg));
+                            args.push(ctx.provided_kind(param, arg));
                             args_iter.next();
                             params.next();
                         }
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 61d5869c19f1..b4cab3330afa 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -296,7 +296,6 @@ pub trait GenericArgsLowerer<'a, 'tcx> {
 
     fn provided_kind(
         &mut self,
-        preceding_args: &[ty::GenericArg<'tcx>],
         param: &ty::GenericParamDef,
         arg: &GenericArg<'tcx>,
     ) -> ty::GenericArg<'tcx>;
@@ -480,7 +479,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             fn provided_kind(
                 &mut self,
-                _preceding_args: &[ty::GenericArg<'tcx>],
                 param: &ty::GenericParamDef,
                 arg: &GenericArg<'tcx>,
             ) -> ty::GenericArg<'tcx> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 8e647ad3c6a4..e84ae65f9032 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1261,7 +1261,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             fn provided_kind(
                 &mut self,
-                _preceding_args: &[ty::GenericArg<'tcx>],
                 param: &ty::GenericParamDef,
                 arg: &GenericArg<'tcx>,
             ) -> ty::GenericArg<'tcx> {
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 880ee83c80a0..5c1c38aeb953 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -413,7 +413,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
 
             fn provided_kind(
                 &mut self,
-                _preceding_args: &[ty::GenericArg<'tcx>],
                 param: &ty::GenericParamDef,
                 arg: &GenericArg<'tcx>,
             ) -> ty::GenericArg<'tcx> {

From ceb09de256f7e2b05788271d2c447ec628c7fcb8 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 13:02:07 +1100
Subject: [PATCH 310/342] Remove an unnecessary lifetime from `RemapLateParam`.

---
 compiler/rustc_hir_analysis/src/check/compare_impl_item.rs  | 6 +++---
 .../src/check/compare_impl_item/refine.rs                   | 3 +--
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 7332888c5f91..8262a79d2e55 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -424,12 +424,12 @@ fn compare_method_predicate_entailment<'tcx>(
     Ok(())
 }
 
-struct RemapLateParam<'a, 'tcx> {
+struct RemapLateParam<'tcx> {
     tcx: TyCtxt<'tcx>,
-    mapping: &'a FxIndexMap,
+    mapping: FxIndexMap,
 }
 
-impl<'tcx> TypeFolder> for RemapLateParam<'_, 'tcx> {
+impl<'tcx> TypeFolder> for RemapLateParam<'tcx> {
     fn cx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index 494e9560c815..0e9e9b48ab30 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -299,8 +299,7 @@ fn report_mismatched_rpitit_signature<'tcx>(
     })
     .collect();
 
-    let mut return_ty =
-        trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping: &mapping });
+    let mut return_ty = trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping });
 
     if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() {
         let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else {

From 40db88979de16d4db91e9fd493b8d79ea4499e67 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 15:30:21 +1100
Subject: [PATCH 311/342] Use `.and` chaining to improve readability.

---
 compiler/rustc_hir_analysis/src/check/wfcheck.rs   | 14 ++++++--------
 compiler/rustc_hir_analysis/src/coherence/mod.rs   | 12 ++++++------
 .../src/impl_wf_check/min_specialization.rs        | 10 ++++------
 3 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 83bb8c0dfaf1..c9837ca3716c 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -2267,14 +2267,12 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
 
 fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), ErrorGuaranteed> {
     let items = tcx.hir_module_items(module);
-    let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id));
-    res =
-        res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)));
-    res =
-        res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)));
-    res = res
-        .and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)));
-    res = res.and(items.par_opaques(|item| tcx.ensure().check_well_formed(item)));
+    let res = items
+        .par_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))
+        .and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)))
+        .and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)))
+        .and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)))
+        .and(items.par_opaques(|item| tcx.ensure().check_well_formed(item)));
     if module == LocalModDefId::CRATE_DEF_ID {
         super::entry::check_for_entry_fn(tcx);
     }
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 4e5f0a3186a9..b1f73a903d81 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -158,12 +158,12 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed>
         let trait_ref = trait_header.trait_ref.instantiate_identity();
         let trait_def = tcx.trait_def(trait_ref.def_id);
 
-        res = res.and(check_impl(tcx, impl_def_id, trait_ref, trait_def));
-        res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref));
-
-        res = res.and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def));
-        res = res.and(tcx.ensure().orphan_check_impl(impl_def_id));
-        res = res.and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header));
+        res = res
+            .and(check_impl(tcx, impl_def_id, trait_ref, trait_def))
+            .and(check_object_overlap(tcx, impl_def_id, trait_ref))
+            .and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def))
+            .and(tcx.ensure().orphan_check_impl(impl_def_id))
+            .and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header));
     }
 
     res
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 32694d5f4bc1..ce10113c4f5b 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -119,7 +119,6 @@ fn check_always_applicable(
     impl2_node: Node,
 ) -> Result<(), ErrorGuaranteed> {
     let span = tcx.def_span(impl1_def_id);
-    let mut res = check_has_items(tcx, impl1_def_id, impl2_node, span);
 
     let (impl1_args, impl2_args) = get_impl_args(tcx, impl1_def_id, impl2_node)?;
     let impl2_def_id = impl2_node.def_id();
@@ -131,11 +130,10 @@ fn check_always_applicable(
         unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args)
     };
 
-    res = res.and(check_static_lifetimes(tcx, &parent_args, span));
-    res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
-    res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
-
-    res
+    check_has_items(tcx, impl1_def_id, impl2_node, span)
+        .and(check_static_lifetimes(tcx, &parent_args, span))
+        .and(check_duplicate_params(tcx, impl1_args, parent_args, span))
+        .and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span))
 }
 
 fn check_has_items(

From d8be00fd44f6d152494ea23734fe692abd47cf36 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 13:23:30 +1100
Subject: [PATCH 312/342] Fix a comment typo.

---
 .../rustc_hir_analysis/src/impl_wf_check/min_specialization.rs  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index ce10113c4f5b..af1107b499f4 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -34,7 +34,7 @@
 //! impl> SpecExtend for I { /* default impl */ }
 //! ```
 //!
-//! We get that the generic pamameters for `impl2` are `[T, std::vec::IntoIter]`.
+//! We get that the generic parameters for `impl2` are `[T, std::vec::IntoIter]`.
 //! `T` is constrained to be `::Item`, so we check only
 //! `std::vec::IntoIter` for repeated parameters, which it doesn't have. The
 //! predicates of `impl1` are only `T: Sized`, which is also a predicate of

From 4e2ef694f357d527eaa7e5982c914ebb32559b4c Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 14:25:13 +1100
Subject: [PATCH 313/342] Remove an unnecessary loop label.

---
 compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index c2377b4781c2..036163b9f140 100644
--- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -24,7 +24,7 @@ pub(super) fn infer_predicates(
 
     // If new predicates were added then we need to re-calculate
     // all crates since there could be new implied predicates.
-    'outer: loop {
+    loop {
         let mut predicates_added = false;
 
         // Visit all the crates and infer predicates
@@ -90,7 +90,7 @@ pub(super) fn infer_predicates(
         }
 
         if !predicates_added {
-            break 'outer;
+            break;
         }
     }
 

From 29317604d1926e526ee89d86398ee6d26ef126a4 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Thu, 30 Jan 2025 14:59:19 +1100
Subject: [PATCH 314/342] Remove `xform` submodule.

It used to be bigger, with an `Xform` trait, but now it has just a
single function.
---
 .../rustc_hir_analysis/src/variance/mod.rs    |  3 ---
 .../rustc_hir_analysis/src/variance/solve.rs  | 20 ++++++++++++++++-
 .../rustc_hir_analysis/src/variance/xform.rs  | 22 -------------------
 3 files changed, 19 insertions(+), 26 deletions(-)
 delete mode 100644 compiler/rustc_hir_analysis/src/variance/xform.rs

diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 02cfb57b836f..a7760326bb4c 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -27,9 +27,6 @@ mod solve;
 
 pub(crate) mod dump;
 
-/// Code for transforming variances.
-mod xform;
-
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers { variances_of, crate_variances, ..*providers };
 }
diff --git a/compiler/rustc_hir_analysis/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs
index d0bdca867792..4106c1a5b635 100644
--- a/compiler/rustc_hir_analysis/src/variance/solve.rs
+++ b/compiler/rustc_hir_analysis/src/variance/solve.rs
@@ -12,8 +12,26 @@ use tracing::debug;
 use super::constraints::*;
 use super::terms::VarianceTerm::*;
 use super::terms::*;
-use super::xform::*;
 
+fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance {
+    // Greatest lower bound of the variance lattice as defined in The Paper:
+    //
+    //       *
+    //    -     +
+    //       o
+    match (v1, v2) {
+        (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant,
+
+        (ty::Covariant, ty::Contravariant) => ty::Invariant,
+        (ty::Contravariant, ty::Covariant) => ty::Invariant,
+
+        (ty::Covariant, ty::Covariant) => ty::Covariant,
+
+        (ty::Contravariant, ty::Contravariant) => ty::Contravariant,
+
+        (x, ty::Bivariant) | (ty::Bivariant, x) => x,
+    }
+}
 struct SolveContext<'a, 'tcx> {
     terms_cx: TermsContext<'a, 'tcx>,
     constraints: Vec>,
diff --git a/compiler/rustc_hir_analysis/src/variance/xform.rs b/compiler/rustc_hir_analysis/src/variance/xform.rs
deleted file mode 100644
index 2e9964788e6e..000000000000
--- a/compiler/rustc_hir_analysis/src/variance/xform.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use rustc_middle::ty;
-
-pub(crate) fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance {
-    // Greatest lower bound of the variance lattice as
-    // defined in The Paper:
-    //
-    //       *
-    //    -     +
-    //       o
-    match (v1, v2) {
-        (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant,
-
-        (ty::Covariant, ty::Contravariant) => ty::Invariant,
-        (ty::Contravariant, ty::Covariant) => ty::Invariant,
-
-        (ty::Covariant, ty::Covariant) => ty::Covariant,
-
-        (ty::Contravariant, ty::Contravariant) => ty::Contravariant,
-
-        (x, ty::Bivariant) | (ty::Bivariant, x) => x,
-    }
-}

From 483307f1271d798a51bb5e4fc8c9f7b731ad6e46 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 29 Jan 2025 10:52:24 +1100
Subject: [PATCH 315/342] Don't export `rustc_hir_analysis::collect`.

Instead re-export `rustc_hir_analysis::collect::suggest_impl_trait`,
which is the only thing from the module used in another crate. This
fixes a `FIXME` comment. Also adjust some visibilities to satisfy the
`unreachable_pub` lint.

This changes requires downgrading a link in a comment on `FnCtxt`
because `collect` is no longer public and rustdoc complains otherwise.
This is annoying but I can't see how to avoid it.
---
 compiler/rustc_hir_analysis/src/collect.rs         | 14 +++++++-------
 compiler/rustc_hir_analysis/src/lib.rs             | 10 +++++-----
 compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs       |  8 ++++----
 .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs    |  2 +-
 4 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index cad7b2a1e57b..226370fccadd 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -57,7 +57,7 @@ mod type_of;
 
 ///////////////////////////////////////////////////////////////////////////
 
-pub fn provide(providers: &mut Providers) {
+pub(crate) fn provide(providers: &mut Providers) {
     resolve_bound_vars::provide(providers);
     *providers = Providers {
         type_of: type_of::type_of,
@@ -122,7 +122,7 @@ pub fn provide(providers: &mut Providers) {
 /// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy
 /// `probe_ty_param_bounds` requests, drawing the information from
 /// the HIR (`hir::Generics`), recursively.
-pub struct ItemCtxt<'tcx> {
+pub(crate) struct ItemCtxt<'tcx> {
     tcx: TyCtxt<'tcx>,
     item_def_id: LocalDefId,
     tainted_by_errors: Cell>,
@@ -148,7 +148,7 @@ impl<'v> Visitor<'v> for HirPlaceholderCollector {
     }
 }
 
-pub struct CollectItemTypesVisitor<'tcx> {
+pub(crate) struct CollectItemTypesVisitor<'tcx> {
     pub tcx: TyCtxt<'tcx>,
 }
 
@@ -364,19 +364,19 @@ fn bad_placeholder<'cx, 'tcx>(
 }
 
 impl<'tcx> ItemCtxt<'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) -> ItemCtxt<'tcx> {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) -> ItemCtxt<'tcx> {
         ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) }
     }
 
-    pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
+    pub(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
         self.lowerer().lower_ty(hir_ty)
     }
 
-    pub fn hir_id(&self) -> hir::HirId {
+    pub(crate) fn hir_id(&self) -> hir::HirId {
         self.tcx.local_def_id_to_hir_id(self.item_def_id)
     }
 
-    pub fn node(&self) -> hir::Node<'tcx> {
+    pub(crate) fn node(&self) -> hir::Node<'tcx> {
         self.tcx.hir_node(self.hir_id())
     }
 
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index bc7d4365eeef..f77bba89d8cf 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -83,12 +83,11 @@ pub mod autoderef;
 mod bounds;
 mod check_unused;
 mod coherence;
-mod delegation;
-pub mod hir_ty_lowering;
-// FIXME: This module shouldn't be public.
-pub mod collect;
+mod collect;
 mod constrained_generic_params;
+mod delegation;
 mod errors;
+pub mod hir_ty_lowering;
 pub mod hir_wf_check;
 mod impl_wf_check;
 mod outlives;
@@ -104,7 +103,8 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt};
 use rustc_span::Span;
 use rustc_trait_selection::traits;
 
-use self::hir_ty_lowering::{FeedConstTy, HirTyLowerer};
+pub use crate::collect::suggest_impl_trait;
+use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer};
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index d432199f0373..b77e6de52ff1 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -31,12 +31,12 @@ use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt};
 /// functions, closures, and `const`s, including performing type inference
 /// with [`InferCtxt`].
 ///
-/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures*
-/// and thus does not perform type inference.
+/// This is in contrast to `rustc_hir_analysis::collect::ItemCtxt`, which is
+/// used to type-check item *signatures* and thus does not perform type
+/// inference.
 ///
-/// See [`ItemCtxt`]'s docs for more.
+/// See `ItemCtxt`'s docs for more.
 ///
-/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt
 /// [`InferCtxt`]: infer::InferCtxt
 pub(crate) struct FnCtxt<'a, 'tcx> {
     pub(super) body_id: LocalDefId,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index aea2e0fd3a30..60f9265bfcc0 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -12,8 +12,8 @@ use rustc_hir::{
     GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind,
     WherePredicateKind, expr_needs_parens,
 };
-use rustc_hir_analysis::collect::suggest_impl_trait;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
+use rustc_hir_analysis::suggest_impl_trait;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::span_bug;

From 635c6d0eacf01c147a7caea27f7f05b75b9932ec Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 09:00:58 +1100
Subject: [PATCH 316/342] Simplify `bug!` and `span_bug!`.

Three rules can be combined into one. I based this change on the
definition of `print!`.
---
 compiler/rustc_middle/src/macros.rs | 28 ++++++++--------------------
 1 file changed, 8 insertions(+), 20 deletions(-)

diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs
index 39816c17b985..300dfb95dcb9 100644
--- a/compiler/rustc_middle/src/macros.rs
+++ b/compiler/rustc_middle/src/macros.rs
@@ -4,8 +4,8 @@
 ///
 /// If you have a span available, you should use [`span_bug`] instead.
 ///
-/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`]
-/// may be useful.
+/// If the bug should only be emitted when compilation didn't fail,
+/// [`DiagCtxtHandle::span_delayed_bug`] may be useful.
 ///
 /// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug
 /// [`span_bug`]: crate::span_bug
@@ -14,14 +14,8 @@ macro_rules! bug {
     () => (
         $crate::bug!("impossible case reached")
     );
-    ($msg:expr) => (
-        $crate::util::bug::bug_fmt(::std::format_args!($msg))
-    );
-    ($msg:expr,) => (
-        $crate::bug!($msg)
-    );
-    ($fmt:expr, $($arg:tt)+) => (
-        $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+))
+    ($($arg:tt)+) => (
+        $crate::util::bug::bug_fmt(::std::format_args!($($arg)+))
     );
 }
 
@@ -30,20 +24,14 @@ macro_rules! bug {
 /// at the code the compiler was compiling when it ICEd. This is the preferred way to trigger
 /// ICEs.
 ///
-/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`]
-/// may be useful.
+/// If the bug should only be emitted when compilation didn't fail,
+/// [`DiagCtxtHandle::span_delayed_bug`] may be useful.
 ///
 /// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug
 #[macro_export]
 macro_rules! span_bug {
-    ($span:expr, $msg:expr) => (
-        $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg))
-    );
-    ($span:expr, $msg:expr,) => (
-        $crate::span_bug!($span, $msg)
-    );
-    ($span:expr, $fmt:expr, $($arg:tt)+) => (
-        $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+))
+    ($span:expr, $($arg:tt)+) => (
+        $crate::util::bug::span_bug_fmt($span, ::std::format_args!($($arg)+))
     );
 }
 

From ba5386aeed08ed4d2a305b8f14c34de800e48167 Mon Sep 17 00:00:00 2001
From: Ryan Cumming 
Date: Fri, 31 Jan 2025 10:23:46 +1100
Subject: [PATCH 317/342] Fix a typo in conventions.md

Introduced in #135950
---
 src/doc/rustc-dev-guide/src/conventions.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md
index 4010e90821f9..0e624a4566d2 100644
--- a/src/doc/rustc-dev-guide/src/conventions.md
+++ b/src/doc/rustc-dev-guide/src/conventions.md
@@ -43,7 +43,7 @@ environment.
 
 ## Formatting and linting Python code
 
-The Rust repository contains quite a lof of Python code. We try to keep
+The Rust repository contains quite a lot of Python code. We try to keep
 it both linted and formatted by the [ruff][ruff] tool.
 
 When modifying Python code, use this command to format it:

From d6e8c7f7a0813c1f8cd42c00cb43d4c1b3f562ac Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 31 Jan 2025 00:11:55 +0000
Subject: [PATCH 318/342] Delay a bug when indexing unsized slices

---
 .../src/check_consts/check.rs                 |  7 +++-
 tests/ui/consts/const-slice-array-deref.rs    |  9 +++++
 .../ui/consts/const-slice-array-deref.stderr  | 33 +++++++++++++++++++
 3 files changed, 48 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/consts/const-slice-array-deref.rs
 create mode 100644 tests/ui/consts/const-slice-array-deref.stderr

diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 16ead1b97854..4834fd3d34c4 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -634,7 +634,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
             Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => {
                 // These are only inserted for slice length, so the place must already be indirect.
                 // This implies we do not have to worry about whether the borrow escapes.
-                assert!(place.is_indirect(), "fake borrows are always indirect");
+                if !place.is_indirect() {
+                    self.tcx.dcx().span_delayed_bug(
+                        self.body.source_info(location).span,
+                        "fake borrows are always indirect",
+                    );
+                }
             }
 
             Rvalue::Cast(
diff --git a/tests/ui/consts/const-slice-array-deref.rs b/tests/ui/consts/const-slice-array-deref.rs
new file mode 100644
index 000000000000..9d84ed4bdb01
--- /dev/null
+++ b/tests/ui/consts/const-slice-array-deref.rs
@@ -0,0 +1,9 @@
+const ONE: [u16] = [1];
+//~^ ERROR the size for values of type `[u16]` cannot be known at compilation time
+//~| ERROR the size for values of type `[u16]` cannot be known at compilation time
+//~| ERROR mismatched types
+
+const TWO: &'static u16 = &ONE[0];
+//~^ ERROR cannot move a value of type `[u16]`
+
+fn main() {}
diff --git a/tests/ui/consts/const-slice-array-deref.stderr b/tests/ui/consts/const-slice-array-deref.stderr
new file mode 100644
index 000000000000..6e69744144ed
--- /dev/null
+++ b/tests/ui/consts/const-slice-array-deref.stderr
@@ -0,0 +1,33 @@
+error[E0277]: the size for values of type `[u16]` cannot be known at compilation time
+  --> $DIR/const-slice-array-deref.rs:1:12
+   |
+LL | const ONE: [u16] = [1];
+   |            ^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[u16]`
+
+error[E0308]: mismatched types
+  --> $DIR/const-slice-array-deref.rs:1:20
+   |
+LL | const ONE: [u16] = [1];
+   |                    ^^^ expected `[u16]`, found `[u16; 1]`
+
+error[E0277]: the size for values of type `[u16]` cannot be known at compilation time
+  --> $DIR/const-slice-array-deref.rs:1:20
+   |
+LL | const ONE: [u16] = [1];
+   |                    ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[u16]`
+   = note: constant expressions must have a statically known size
+
+error[E0161]: cannot move a value of type `[u16]`
+  --> $DIR/const-slice-array-deref.rs:6:28
+   |
+LL | const TWO: &'static u16 = &ONE[0];
+   |                            ^^^ the size of `[u16]` cannot be statically determined
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0161, E0277, E0308.
+For more information about an error, try `rustc --explain E0161`.

From 63a039a7f5b5da9e438943b9d55985e9c514c0b5 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Fri, 31 Jan 2025 12:51:56 +1100
Subject: [PATCH 319/342] Replace our `LLVMRustDIBuilderRef` with LLVM-C's
 `LLVMDIBuilderRef`

This makes it possible to start incrementally replacing our debuginfo bindings
with the ones in the LLVM-C API, all of which operate on `LLVMDIBuilderRef`.
---
 .../rustc_llvm/llvm-wrapper/RustWrapper.cpp   | 181 +++++++++---------
 1 file changed, 91 insertions(+), 90 deletions(-)

diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 35186778671b..abc571346594 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -667,8 +667,6 @@ extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,
       unwrap(Ty), StringRef(Constraints, ConstraintsLen)));
 }
 
-typedef DIBuilder *LLVMRustDIBuilderRef;
-
 template  DIT *unwrapDIPtr(LLVMMetadataRef Ref) {
   return (DIT *)(Ref ? unwrap(Ref) : nullptr);
 }
@@ -994,34 +992,34 @@ extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind,
   unwrap(Global)->addMetadata(Kind, *unwrap(MD));
 }
 
-extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) {
-  return new DIBuilder(*unwrap(M));
+extern "C" LLVMDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) {
+  return wrap(new DIBuilder(*unwrap(M)));
 }
 
-extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) {
-  delete Builder;
+extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) {
+  delete unwrap(Builder);
 }
 
-extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) {
-  Builder->finalize();
+extern "C" void LLVMRustDIBuilderFinalize(LLVMDIBuilderRef Builder) {
+  unwrap(Builder)->finalize();
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit(
-    LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef,
+    LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef,
     const char *Producer, size_t ProducerLen, bool isOptimized,
     const char *Flags, unsigned RuntimeVer, const char *SplitName,
     size_t SplitNameLen, LLVMRustDebugEmissionKind Kind, uint64_t DWOId,
     bool SplitDebugInlining, LLVMRustDebugNameTableKind TableKind) {
   auto *File = unwrapDI(FileRef);
 
-  return wrap(Builder->createCompileUnit(
+  return wrap(unwrap(Builder)->createCompileUnit(
       Lang, File, StringRef(Producer, ProducerLen), isOptimized, Flags,
       RuntimeVer, StringRef(SplitName, SplitNameLen), fromRust(Kind), DWOId,
       SplitDebugInlining, false, fromRust(TableKind)));
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateFile(LLVMRustDIBuilderRef Builder, const char *Filename,
+LLVMRustDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename,
                             size_t FilenameLen, const char *Directory,
                             size_t DirectoryLen, LLVMRustChecksumKind CSKind,
                             const char *Checksum, size_t ChecksumLen,
@@ -1034,20 +1032,20 @@ LLVMRustDIBuilderCreateFile(LLVMRustDIBuilderRef Builder, const char *Filename,
   std::optional oSource{};
   if (Source)
     oSource = StringRef(Source, SourceLen);
-  return wrap(Builder->createFile(StringRef(Filename, FilenameLen),
-                                  StringRef(Directory, DirectoryLen), CSInfo,
-                                  oSource));
+  return wrap(unwrap(Builder)->createFile(StringRef(Filename, FilenameLen),
+                                          StringRef(Directory, DirectoryLen),
+                                          CSInfo, oSource));
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder,
+LLVMRustDIBuilderCreateSubroutineType(LLVMDIBuilderRef Builder,
                                       LLVMMetadataRef ParameterTypes) {
-  return wrap(Builder->createSubroutineType(
+  return wrap(unwrap(Builder)->createSubroutineType(
       DITypeRefArray(unwrap(ParameterTypes))));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
     LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
     unsigned ScopeLine, LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags,
@@ -1056,7 +1054,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
       DITemplateParameterArray(unwrap(TParam));
   DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
   DINode::DIFlags llvmFlags = fromRust(Flags);
-  DISubprogram *Sub = Builder->createFunction(
+  DISubprogram *Sub = unwrap(Builder)->createFunction(
       unwrapDI(Scope), StringRef(Name, NameLen),
       StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo,
       unwrapDI(Ty), ScopeLine, llvmFlags, llvmSPFlags,
@@ -1067,7 +1065,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
     LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
     LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
@@ -1075,7 +1073,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
       DITemplateParameterArray(unwrap(TParam));
   DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
   DINode::DIFlags llvmFlags = fromRust(Flags);
-  DISubprogram *Sub = Builder->createMethod(
+  DISubprogram *Sub = unwrap(Builder)->createMethod(
       unwrapDI(Scope), StringRef(Name, NameLen),
       StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo,
       unwrapDI(Ty), 0, 0,
@@ -1085,39 +1083,39 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateBasicType(LLVMRustDIBuilderRef Builder, const char *Name,
+LLVMRustDIBuilderCreateBasicType(LLVMDIBuilderRef Builder, const char *Name,
                                  size_t NameLen, uint64_t SizeInBits,
                                  unsigned Encoding) {
-  return wrap(
-      Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding));
+  return wrap(unwrap(Builder)->createBasicType(StringRef(Name, NameLen),
+                                               SizeInBits, Encoding));
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateTypedef(LLVMRustDIBuilderRef Builder,
-                               LLVMMetadataRef Type, const char *Name,
-                               size_t NameLen, LLVMMetadataRef File,
-                               unsigned LineNo, LLVMMetadataRef Scope) {
-  return wrap(Builder->createTypedef(
+LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type,
+                               const char *Name, size_t NameLen,
+                               LLVMMetadataRef File, unsigned LineNo,
+                               LLVMMetadataRef Scope) {
+  return wrap(unwrap(Builder)->createTypedef(
       unwrap(Type), StringRef(Name, NameLen), unwrap(File),
       LineNo, unwrapDIPtr(Scope)));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy,
-    uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace,
-    const char *Name, size_t NameLen) {
-  return wrap(Builder->createPointerType(unwrapDI(PointeeTy),
-                                         SizeInBits, AlignInBits, AddressSpace,
-                                         StringRef(Name, NameLen)));
+    LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy, uint64_t SizeInBits,
+    uint32_t AlignInBits, unsigned AddressSpace, const char *Name,
+    size_t NameLen) {
+  return wrap(unwrap(Builder)->createPointerType(
+      unwrapDI(PointeeTy), SizeInBits, AlignInBits, AddressSpace,
+      StringRef(Name, NameLen)));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
     uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags,
     LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang,
     LLVMMetadataRef VTableHolder, const char *UniqueId, size_t UniqueIdLen) {
-  return wrap(Builder->createStructType(
+  return wrap(unwrap(Builder)->createStructType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNumber, SizeInBits, AlignInBits,
       fromRust(Flags), unwrapDI(DerivedFrom),
@@ -1126,12 +1124,12 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType(
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
     uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags,
     LLVMMetadataRef Discriminator, LLVMMetadataRef Elements,
     const char *UniqueId, size_t UniqueIdLen) {
-  return wrap(Builder->createVariantPart(
+  return wrap(unwrap(Builder)->createVariantPart(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNumber, SizeInBits, AlignInBits,
       fromRust(Flags), unwrapDI(Discriminator),
@@ -1140,18 +1138,18 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart(
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits,
     uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags,
     LLVMMetadataRef Ty) {
-  return wrap(Builder->createMemberType(
+  return wrap(unwrap(Builder)->createMemberType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits,
       fromRust(Flags), unwrapDI(Ty)));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits,
     uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant,
     LLVMRustDIFlags Flags, LLVMMetadataRef Ty) {
@@ -1159,17 +1157,17 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
   if (Discriminant) {
     D = unwrap(Discriminant);
   }
-  return wrap(Builder->createVariantMemberType(
+  return wrap(unwrap(Builder)->createVariantMemberType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, D,
       fromRust(Flags), unwrapDI(Ty)));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
     LLVMRustDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) {
-  return wrap(Builder->createStaticMemberType(
+  return wrap(unwrap(Builder)->createStaticMemberType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNo, unwrapDI(Ty), fromRust(Flags),
       unwrap(val), llvm::dwarf::DW_TAG_member, AlignInBits));
@@ -1183,21 +1181,21 @@ LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag,
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateLexicalBlock(LLVMRustDIBuilderRef Builder,
+LLVMRustDIBuilderCreateLexicalBlock(LLVMDIBuilderRef Builder,
                                     LLVMMetadataRef Scope, LLVMMetadataRef File,
                                     unsigned Line, unsigned Col) {
-  return wrap(Builder->createLexicalBlock(unwrapDI(Scope),
-                                          unwrapDI(File), Line, Col));
+  return wrap(unwrap(Builder)->createLexicalBlock(
+      unwrapDI(Scope), unwrapDI(File), Line, Col));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) {
-  return wrap(Builder->createLexicalBlockFile(unwrapDI(Scope),
-                                              unwrapDI(File)));
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) {
+  return wrap(unwrap(Builder)->createLexicalBlockFile(
+      unwrapDI(Scope), unwrapDI(File)));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name,
     size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
     LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
     bool IsLocalToUnit, LLVMValueRef V, LLVMMetadataRef Decl = nullptr,
@@ -1206,16 +1204,16 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
 
   llvm::DIExpression *InitExpr = nullptr;
   if (llvm::ConstantInt *IntVal = llvm::dyn_cast(InitVal)) {
-    InitExpr = Builder->createConstantValueExpression(
+    InitExpr = unwrap(Builder)->createConstantValueExpression(
         IntVal->getValue().getSExtValue());
   } else if (llvm::ConstantFP *FPVal =
                  llvm::dyn_cast(InitVal)) {
-    InitExpr = Builder->createConstantValueExpression(
+    InitExpr = unwrap(Builder)->createConstantValueExpression(
         FPVal->getValueAPF().bitcastToAPInt().getZExtValue());
   }
 
   llvm::DIGlobalVariableExpression *VarExpr =
-      Builder->createGlobalVariableExpression(
+      unwrap(Builder)->createGlobalVariableExpression(
           unwrapDI(Context), StringRef(Name, NameLen),
           StringRef(LinkageName, LinkageNameLen), unwrapDI(File),
           LineNo, unwrapDI(Ty), IsLocalToUnit,
@@ -1228,17 +1226,17 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable(
-    LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope,
+    LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope,
     const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo,
     LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags,
     unsigned ArgNo, uint32_t AlignInBits) {
   if (Tag == 0x100) { // DW_TAG_auto_variable
-    return wrap(Builder->createAutoVariable(
+    return wrap(unwrap(Builder)->createAutoVariable(
         unwrapDI(Scope), StringRef(Name, NameLen),
         unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve,
         fromRust(Flags), AlignInBits));
   } else {
-    return wrap(Builder->createParameterVariable(
+    return wrap(unwrap(Builder)->createParameterVariable(
         unwrapDI(Scope), StringRef(Name, NameLen), ArgNo,
         unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve,
         fromRust(Flags)));
@@ -1246,53 +1244,56 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable(
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size,
+LLVMRustDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size,
                                  uint32_t AlignInBits, LLVMMetadataRef Ty,
                                  LLVMMetadataRef Subscripts) {
-  return wrap(
-      Builder->createArrayType(Size, AlignInBits, unwrapDI(Ty),
-                               DINodeArray(unwrapDI(Subscripts))));
+  return wrap(unwrap(Builder)->createArrayType(
+      Size, AlignInBits, unwrapDI(Ty),
+      DINodeArray(unwrapDI(Subscripts))));
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo,
+LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo,
                                      int64_t Count) {
-  return wrap(Builder->getOrCreateSubrange(Lo, Count));
+  return wrap(unwrap(Builder)->getOrCreateSubrange(Lo, Count));
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder,
+LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder,
                                   LLVMMetadataRef *Ptr, unsigned Count) {
   Metadata **DataValue = unwrap(Ptr);
-  return wrap(
-      Builder->getOrCreateArray(ArrayRef(DataValue, Count)).get());
+  return wrap(unwrap(Builder)
+                  ->getOrCreateArray(ArrayRef(DataValue, Count))
+                  .get());
 }
 
-extern "C" void LLVMRustDIBuilderInsertDeclareAtEnd(
-    LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo,
-    uint64_t *AddrOps, unsigned AddrOpsCount, LLVMMetadataRef DL,
-    LLVMBasicBlockRef InsertAtEnd) {
-  Builder->insertDeclare(unwrap(V), unwrap(VarInfo),
-                         Builder->createExpression(
-                             llvm::ArrayRef(AddrOps, AddrOpsCount)),
-                         DebugLoc(cast(unwrap(DL))),
-                         unwrap(InsertAtEnd));
+extern "C" void
+LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V,
+                                    LLVMMetadataRef VarInfo, uint64_t *AddrOps,
+                                    unsigned AddrOpsCount, LLVMMetadataRef DL,
+                                    LLVMBasicBlockRef InsertAtEnd) {
+  unwrap(Builder)->insertDeclare(
+      unwrap(V), unwrap(VarInfo),
+      unwrap(Builder)->createExpression(
+          llvm::ArrayRef(AddrOps, AddrOpsCount)),
+      DebugLoc(cast(unwrap(DL))), unwrap(InsertAtEnd));
 }
 
-extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(
-    LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen,
-    const uint64_t Value[2], unsigned SizeInBits, bool IsUnsigned) {
-  return wrap(Builder->createEnumerator(
+extern "C" LLVMMetadataRef
+LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name,
+                                  size_t NameLen, const uint64_t Value[2],
+                                  unsigned SizeInBits, bool IsUnsigned) {
+  return wrap(unwrap(Builder)->createEnumerator(
       StringRef(Name, NameLen),
       APSInt(APInt(SizeInBits, ArrayRef(Value, 2)), IsUnsigned)));
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
     uint64_t SizeInBits, uint32_t AlignInBits, LLVMMetadataRef Elements,
     LLVMMetadataRef ClassTy, bool IsScoped) {
-  return wrap(Builder->createEnumerationType(
+  return wrap(unwrap(Builder)->createEnumerationType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNumber, SizeInBits, AlignInBits,
       DINodeArray(unwrapDI(Elements)), unwrapDI(ClassTy),
@@ -1300,12 +1301,12 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType(
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
     uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags,
     LLVMMetadataRef Elements, unsigned RunTimeLang, const char *UniqueId,
     size_t UniqueIdLen) {
-  return wrap(Builder->createUnionType(
+  return wrap(unwrap(Builder)->createUnionType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNumber, SizeInBits, AlignInBits,
       fromRust(Flags), DINodeArray(unwrapDI(Elements)), RunTimeLang,
@@ -1313,28 +1314,28 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType(
 }
 
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef Ty) {
   bool IsDefault = false; // FIXME: should we ever set this true?
-  return wrap(Builder->createTemplateTypeParameter(
+  return wrap(unwrap(Builder)->createTemplateTypeParameter(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(Ty), IsDefault));
 }
 
 extern "C" LLVMMetadataRef
-LLVMRustDIBuilderCreateNameSpace(LLVMRustDIBuilderRef Builder,
+LLVMRustDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder,
                                  LLVMMetadataRef Scope, const char *Name,
                                  size_t NameLen, bool ExportSymbols) {
-  return wrap(Builder->createNameSpace(
+  return wrap(unwrap(Builder)->createNameSpace(
       unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols));
 }
 
 extern "C" void LLVMRustDICompositeTypeReplaceArrays(
-    LLVMRustDIBuilderRef Builder, LLVMMetadataRef CompositeTy,
+    LLVMDIBuilderRef Builder, LLVMMetadataRef CompositeTy,
     LLVMMetadataRef Elements, LLVMMetadataRef Params) {
   DICompositeType *Tmp = unwrapDI(CompositeTy);
-  Builder->replaceArrays(Tmp, DINodeArray(unwrap(Elements)),
-                         DINodeArray(unwrap(Params)));
+  unwrap(Builder)->replaceArrays(Tmp, DINodeArray(unwrap(Elements)),
+                                 DINodeArray(unwrap(Params)));
 }
 
 extern "C" LLVMMetadataRef

From bf15d599ffa050bd8ef022e8f656b1336675b203 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Fri, 31 Jan 2025 12:53:57 +1100
Subject: [PATCH 320/342] Remove the temporary typedef for `LLVMRustDIFlags`

If we're already churning all of the debuginfo bindings, we might as well fix
this at the same time.
---
 .../rustc_llvm/llvm-wrapper/RustWrapper.cpp   | 26 +++++++------------
 1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index abc571346594..74327334537b 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -675,12 +675,6 @@ template  DIT *unwrapDIPtr(LLVMMetadataRef Ref) {
 #define DIArray DINodeArray
 #define unwrapDI unwrapDIPtr
 
-// FIXME(Zalathar): This is a temporary typedef to avoid churning dozens of
-// bindings that are going to be deleted and replaced with their LLVM-C
-// equivalents, as part of #134009. After that happens, the remaining bindings
-// can be adjusted to use `LLVMDIFlags` instead of relying on this typedef.
-typedef LLVMDIFlags LLVMRustDIFlags;
-
 // Statically assert that `LLVMDIFlags` (C) and `DIFlags` (C++) have the same
 // layout, at least for the flags we know about. This isn't guaranteed, but is
 // likely to remain true, and as long as it is true it makes conversions easy.
@@ -1048,7 +1042,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
     LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
-    unsigned ScopeLine, LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags,
+    unsigned ScopeLine, LLVMDIFlags Flags, LLVMRustDISPFlags SPFlags,
     LLVMValueRef MaybeFn, LLVMMetadataRef TParam, LLVMMetadataRef Decl) {
   DITemplateParameterArray TParams =
       DITemplateParameterArray(unwrap(TParam));
@@ -1068,7 +1062,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
     LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
-    LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
+    LLVMDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) {
   DITemplateParameterArray TParams =
       DITemplateParameterArray(unwrap(TParam));
   DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags);
@@ -1112,7 +1106,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType(
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
-    uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags,
+    uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags,
     LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang,
     LLVMMetadataRef VTableHolder, const char *UniqueId, size_t UniqueIdLen) {
   return wrap(unwrap(Builder)->createStructType(
@@ -1126,7 +1120,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType(
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
-    uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags,
+    uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags,
     LLVMMetadataRef Discriminator, LLVMMetadataRef Elements,
     const char *UniqueId, size_t UniqueIdLen) {
   return wrap(unwrap(Builder)->createVariantPart(
@@ -1140,7 +1134,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart(
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits,
-    uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags,
+    uint32_t AlignInBits, uint64_t OffsetInBits, LLVMDIFlags Flags,
     LLVMMetadataRef Ty) {
   return wrap(unwrap(Builder)->createMemberType(
       unwrapDI(Scope), StringRef(Name, NameLen),
@@ -1152,7 +1146,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits,
     uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant,
-    LLVMRustDIFlags Flags, LLVMMetadataRef Ty) {
+    LLVMDIFlags Flags, LLVMMetadataRef Ty) {
   llvm::ConstantInt *D = nullptr;
   if (Discriminant) {
     D = unwrap(Discriminant);
@@ -1166,7 +1160,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
-    LLVMRustDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) {
+    LLVMDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) {
   return wrap(unwrap(Builder)->createStaticMemberType(
       unwrapDI(Scope), StringRef(Name, NameLen),
       unwrapDI(File), LineNo, unwrapDI(Ty), fromRust(Flags),
@@ -1228,8 +1222,8 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable(
     LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope,
     const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo,
-    LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags,
-    unsigned ArgNo, uint32_t AlignInBits) {
+    LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMDIFlags Flags, unsigned ArgNo,
+    uint32_t AlignInBits) {
   if (Tag == 0x100) { // DW_TAG_auto_variable
     return wrap(unwrap(Builder)->createAutoVariable(
         unwrapDI(Scope), StringRef(Name, NameLen),
@@ -1303,7 +1297,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType(
 extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType(
     LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
     size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
-    uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags,
+    uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags,
     LLVMMetadataRef Elements, unsigned RunTimeLang, const char *UniqueId,
     size_t UniqueIdLen) {
   return wrap(unwrap(Builder)->createUnionType(

From be1aa7bb2a417ea87cea83b500bc3005d4f71e6a Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 15:03:50 +1100
Subject: [PATCH 321/342] Add/clarify comments about hooks.

Explaining things that weren't clear to me at first.
---
 compiler/rustc_middle/src/hooks/mod.rs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs
index 92fa64b0987f..f546bf6cad50 100644
--- a/compiler/rustc_middle/src/hooks/mod.rs
+++ b/compiler/rustc_middle/src/hooks/mod.rs
@@ -1,7 +1,7 @@
-//! "Hooks" provide a way for `tcx` functionality to be provided by some downstream crate without
-//! everything in rustc having to depend on that crate. This is somewhat similar to queries, but
-//! queries come with a lot of machinery for caching and incremental compilation, whereas hooks are
-//! just plain function pointers without any of the query magic.
+//! "Hooks" let you write `tcx` methods in downstream crates and call them in this crate, reducing
+//! the amount of code that needs to be in this crate (which is already very big). This is somewhat
+//! similar to queries, but queries come with a lot of machinery for caching and incremental
+//! compilation, whereas hooks are just plain function pointers without any of the query magic.
 
 use rustc_hir::def_id::{DefId, DefPathHash};
 use rustc_session::StableCrateId;
@@ -107,6 +107,9 @@ declare_hooks! {
 
     /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
     /// can just link to the upstream crate and therefore don't need a mono item.
+    ///
+    /// Note: this hook isn't called within `rustc_middle` but #127779 suggests it's a hook instead
+    /// of a normal function because external tools might want to override it.
     hook should_codegen_locally(instance: crate::ty::Instance<'tcx>) -> bool;
 
     hook alloc_self_profile_query_strings() -> ();

From 4b025ca083948c573a6b4affc0dedf3e4275dd4d Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 14:42:19 +1100
Subject: [PATCH 322/342] Remove the `thir_{tree,flat}` hooks.

They were downgraded from queries in #123995 but they can just be
vanilla functions because they are not called in `rustc_middle`.
---
 compiler/rustc_driver_impl/src/pretty.rs   |  5 +++--
 compiler/rustc_middle/src/hooks/mod.rs     |  6 ------
 compiler/rustc_mir_build/src/lib.rs        |  4 +---
 compiler/rustc_mir_build/src/thir/mod.rs   |  2 +-
 compiler/rustc_mir_build/src/thir/print.rs | 12 +++++++-----
 5 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index 93f3d2ab911f..699742aa0ea1 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -7,6 +7,7 @@ use rustc_ast_pretty::pprust as pprust_ast;
 use rustc_middle::bug;
 use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_mir_build::thir::print::{thir_flat, thir_tree};
 use rustc_session::Session;
 use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode};
 use rustc_smir::rustc_internal::pretty::write_smir_pretty;
@@ -313,7 +314,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
             tcx.dcx().abort_if_errors();
             debug!("pretty printing THIR tree");
             for did in tcx.hir().body_owners() {
-                let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did));
+                let _ = writeln!(out, "{:?}:\n{}\n", did, thir_tree(tcx, did));
             }
             out
         }
@@ -324,7 +325,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
             tcx.dcx().abort_if_errors();
             debug!("pretty printing THIR flat");
             for did in tcx.hir().body_owners() {
-                let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did));
+                let _ = writeln!(out, "{:?}:\n{}\n", did, thir_flat(tcx, did));
             }
             out
         }
diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs
index f546bf6cad50..03bbac342eb8 100644
--- a/compiler/rustc_middle/src/hooks/mod.rs
+++ b/compiler/rustc_middle/src/hooks/mod.rs
@@ -99,12 +99,6 @@ declare_hooks! {
     /// Will fetch a DefId from a DefPathHash for a foreign crate.
     hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId;
 
-    /// Create a THIR tree for debugging.
-    hook thir_tree(key: LocalDefId) -> String;
-
-    /// Create a list-like THIR representation for debugging.
-    hook thir_flat(key: LocalDefId) -> String;
-
     /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
     /// can just link to the upstream crate and therefore don't need a mono item.
     ///
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 8e786733ee03..d8e541217beb 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -18,7 +18,7 @@ mod builder;
 mod check_tail_calls;
 mod check_unsafety;
 mod errors;
-mod thir;
+pub mod thir;
 
 use rustc_middle::util::Providers;
 
@@ -33,6 +33,4 @@ pub fn provide(providers: &mut Providers) {
     providers.check_unsafety = check_unsafety::check_unsafety;
     providers.check_tail_calls = check_tail_calls::check_tail_calls;
     providers.thir_body = thir::cx::thir_body;
-    providers.hooks.thir_tree = thir::print::thir_tree;
-    providers.hooks.thir_flat = thir::print::thir_flat;
 }
diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs
index ca26cc13b5e8..c7c88801d4d8 100644
--- a/compiler/rustc_mir_build/src/thir/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/mod.rs
@@ -7,5 +7,5 @@
 pub(crate) mod constant;
 pub(crate) mod cx;
 pub(crate) mod pattern;
-pub(crate) mod print;
+pub mod print;
 mod util;
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index 2bcdb67c58a9..228a64c2c70d 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -1,12 +1,13 @@
 use std::fmt::{self, Write};
 
-use rustc_middle::query::TyCtxtAt;
 use rustc_middle::thir::*;
 use rustc_middle::ty;
+use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::LocalDefId;
 
-pub(crate) fn thir_tree(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String {
-    match super::cx::thir_body(*tcx, owner_def) {
+/// Create a THIR tree for debugging.
+pub fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String {
+    match super::cx::thir_body(tcx, owner_def) {
         Ok((thir, _)) => {
             let thir = thir.steal();
             let mut printer = ThirPrinter::new(&thir);
@@ -17,8 +18,9 @@ pub(crate) fn thir_tree(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String {
     }
 }
 
-pub(crate) fn thir_flat(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String {
-    match super::cx::thir_body(*tcx, owner_def) {
+/// Create a list-like THIR representation for debugging.
+pub fn thir_flat(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String {
+    match super::cx::thir_body(tcx, owner_def) {
         Ok((thir, _)) => format!("{:#?}", thir.steal()),
         Err(_) => "error".into(),
     }

From 1d2cb611f7320849cd14e2af60de9335d3db7dbe Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 14:50:42 +1100
Subject: [PATCH 323/342] Remove the `mir_build` hook.

It was downgraded from a query in #122721 but it can just be a vanilla
function because it's not called in `rustc_middle`.
---
 compiler/rustc_middle/src/hooks/mod.rs             | 6 ------
 compiler/rustc_mir_build/src/builder/custom/mod.rs | 2 +-
 compiler/rustc_mir_build/src/builder/mod.rs        | 7 +++----
 compiler/rustc_mir_build/src/lib.rs                | 3 +--
 compiler/rustc_mir_transform/src/lib.rs            | 3 ++-
 5 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs
index 03bbac342eb8..2be242364c11 100644
--- a/compiler/rustc_middle/src/hooks/mod.rs
+++ b/compiler/rustc_middle/src/hooks/mod.rs
@@ -75,12 +75,6 @@ declare_hooks! {
     /// (Eligible functions might nevertheless be skipped for other reasons.)
     hook is_eligible_for_coverage(key: LocalDefId) -> bool;
 
-    /// Create the MIR for a given `DefId` - this includes
-    /// unreachable code.
-    /// You do not want to call this yourself, instead use the cached version
-    /// via `mir_built`
-    hook build_mir(key: LocalDefId) -> mir::Body<'tcx>;
-
     /// Imports all `SourceFile`s from the given crate into the current session.
     /// This normally happens automatically when we decode a `Span` from
     /// that crate's metadata - however, the incr comp cache needs
diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs
index ca2e1dd7a1a8..bfc16816e2e5 100644
--- a/compiler/rustc_mir_build/src/builder/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs
@@ -6,7 +6,7 @@
 //! present, and if so we branch off into this module, which implements the attribute by
 //! implementing a custom lowering from THIR to MIR.
 //!
-//! The result of this lowering is returned "normally" from the `build_mir` hook, with the only
+//! The result of this lowering is returned "normally" from `build_mir`, with the only
 //! notable difference being that the `injected` field in the body is set. Various components of the
 //! MIR pipeline, like borrowck and the pass manager will then consult this field (via
 //! `body.should_skip()`) to skip the parts of the MIR pipeline that precede the MIR phase the user
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index 9fa431f7d5fd..93fb9873e80a 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -20,7 +20,6 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
 use rustc_middle::middle::region;
 use rustc_middle::mir::*;
-use rustc_middle::query::TyCtxtAt;
 use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir};
 use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode};
 use rustc_middle::{bug, span_bug};
@@ -45,9 +44,9 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
         .collect()
 }
 
-/// Construct the MIR for a given `DefId`.
-pub(crate) fn build_mir<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
-    let tcx = tcx.tcx;
+/// Create the MIR for a given `DefId`, including unreachable code. Do not call
+/// this directly; instead use the cached version via `mir_built`.
+pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> {
     tcx.ensure_with_value().thir_abstract_const(def);
     if let Err(e) = tcx.check_match(def) {
         return construct_error(tcx, def, e);
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index d8e541217beb..fa5db32d9134 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -14,7 +14,7 @@
 // The `builder` module used to be named `build`, but that was causing GitHub's
 // "Go to file" feature to silently ignore all files in the module, probably
 // because it assumes that "build" is a build-output directory. See #134365.
-mod builder;
+pub mod builder;
 mod check_tail_calls;
 mod check_unsafety;
 mod errors;
@@ -27,7 +27,6 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 pub fn provide(providers: &mut Providers) {
     providers.check_match = thir::pattern::check_match;
     providers.lit_to_const = thir::constant::lit_to_const;
-    providers.hooks.build_mir = builder::build_mir;
     providers.closure_saved_names_of_captured_variables =
         builder::closure_saved_names_of_captured_variables;
     providers.check_unsafety = check_unsafety::check_unsafety;
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 2dc55e3614e6..2b356162f022 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -33,6 +33,7 @@ use rustc_middle::mir::{
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
 use rustc_middle::util::Providers;
 use rustc_middle::{bug, query, span_bug};
+use rustc_mir_build::builder::build_mir;
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, sym};
 use tracing::debug;
@@ -368,7 +369,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
 }
 
 fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> {
-    let mut body = tcx.build_mir(def);
+    let mut body = build_mir(tcx, def);
 
     pass_manager::dump_mir_for_phase_change(tcx, &body);
 

From 09f68486bdcca5eadc9147c57953bd0fd37bb1db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Mon, 20 Jan 2025 19:10:15 +0800
Subject: [PATCH 324/342] run-make-support: add `-Csymbol-mangling-version` and
 `-Cprefer-dynamic` helpers to rustc

Co-authored-by: binarycat 
---
 .../run-make-support/src/external_deps/rustc.rs      | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index b70db7130f67..8d99924a2d17 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -216,6 +216,18 @@ impl Rustc {
         self
     }
 
+    /// Specify option of `-C symbol-mangling-version`.
+    pub fn symbol_mangling_version(&mut self, option: &str) -> &mut Self {
+        self.cmd.arg(format!("-Csymbol-mangling-version={option}"));
+        self
+    }
+
+    /// Specify `-C prefer-dynamic`.
+    pub fn prefer_dynamic(&mut self) -> &mut Self {
+        self.cmd.arg(format!("-Cprefer-dynamic"));
+        self
+    }
+
     /// Specify error format to use
     pub fn error_format(&mut self, format: &str) -> &mut Self {
         self.cmd.arg(format!("--error-format={format}"));

From 17e0fc6f03e841aeba1304af570a06d4e21a9492 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Mon, 20 Jan 2025 19:12:09 +0800
Subject: [PATCH 325/342] run-make-support: collapse re-export

---
 src/tools/run-make-support/src/lib.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 7316244b3841..245c8d52f4d9 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -47,7 +47,9 @@ pub use wasmparser;
 // tidy-alphabetical-end
 
 // Re-exports of external dependencies.
-pub use external_deps::{c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc};
+pub use external_deps::{
+    cargo, c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc
+};
 
 // These rely on external dependencies.
 pub use c_cxx_compiler::{Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc};
@@ -104,4 +106,3 @@ pub use assertion_helpers::{
 pub use string::{
     count_regex_matches_in_files_with_extension, invalid_utf8_contains, invalid_utf8_not_contains,
 };
-use crate::external_deps::cargo;

From c15c9702d6cf1fe36bd8bef7176a98137d6028b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Mon, 20 Jan 2025 19:12:39 +0800
Subject: [PATCH 326/342] run-make-support: add `is_windows_gnu` helper

---
 src/tools/run-make-support/src/lib.rs     | 5 ++++-
 src/tools/run-make-support/src/targets.rs | 6 ++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 245c8d52f4d9..a8c9bec57fde 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -81,7 +81,10 @@ pub use env::{env_var, env_var_os, set_current_dir};
 pub use run::{cmd, run, run_fail, run_with_args};
 
 /// Helpers for checking target information.
-pub use targets::{is_aix, is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
+pub use targets::{
+    apple_os, is_aix, is_darwin, is_msvc, is_windows, is_windows_gnu, llvm_components_contain,
+    target, uname,
+};
 
 /// Helpers for building names of output artifacts that are potentially target-specific.
 pub use artifact_names::{
diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs
index ae004fd0cbdd..a16fca71d2ee 100644
--- a/src/tools/run-make-support/src/targets.rs
+++ b/src/tools/run-make-support/src/targets.rs
@@ -22,6 +22,12 @@ pub fn is_msvc() -> bool {
     target().contains("msvc")
 }
 
+/// Check if target is windows-gnu.
+#[must_use]
+pub fn is_windows_gnu() -> bool {
+    target().ends_with("windows-gnu")
+}
+
 /// Check if target uses macOS.
 #[must_use]
 pub fn is_darwin() -> bool {

From 0bc1b4f4f6ace5b5e2a7676e498343cfeafab136 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Mon, 20 Jan 2025 19:13:16 +0800
Subject: [PATCH 327/342] run-make-support: add `object`-based symbol helpers

- `dynamic_symbol_names`
- `text_section_global_dynamic_symbol_names`
- `global_undefined_dynamic_symbol_names`

Also add some missing `#[track_caller]` attributes.

Co-authored-by: binarycat 
---
 src/tools/run-make-support/src/symbols.rs | 34 +++++++++++++++++------
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs
index fd0c866bcc92..e4d244e14a4a 100644
--- a/src/tools/run-make-support/src/symbols.rs
+++ b/src/tools/run-make-support/src/symbols.rs
@@ -2,28 +2,44 @@ use std::path::Path;
 
 use object::{self, Object, ObjectSymbol, SymbolIterator};
 
-/// Iterate through the symbols in an object file.
+/// Given an [`object::File`], find the exported dynamic symbol names via
+/// [`object::Object::exports`]. This does not distinguish between which section the symbols appear
+/// in.
+#[track_caller]
+pub fn exported_dynamic_symbol_names<'file>(file: &'file object::File<'file>) -> Vec<&'file str> {
+    file.exports()
+        .unwrap()
+        .into_iter()
+        .filter_map(|sym| std::str::from_utf8(sym.name()).ok())
+        .collect()
+}
+
+/// Iterate through the symbols in an object file. See [`object::Object::symbols`].
 ///
-/// Uses a callback because `SymbolIterator` does not own its data.
-///
-/// Panics if `path` is not a valid object file readable by the current user.
+/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
+/// parsed as a recognized object file.
+#[track_caller]
 pub fn with_symbol_iter(path: P, func: F) -> R
 where
     P: AsRef,
     F: FnOnce(&mut SymbolIterator<'_, '_>) -> R,
 {
-    let raw_bytes = crate::fs::read(path);
-    let f = object::File::parse(raw_bytes.as_slice()).expect("unable to parse file");
+    let path = path.as_ref();
+    let blob = crate::fs::read(path);
+    let f = object::File::parse(&*blob)
+        .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
     let mut iter = f.symbols();
     func(&mut iter)
 }
 
 /// Check an object file's symbols for substrings.
 ///
-/// Returns `true` if any of the symbols found in the object file at
-/// `path` contain a substring listed in `substrings`.
+/// Returns `true` if any of the symbols found in the object file at `path` contain a substring
+/// listed in `substrings`.
 ///
-/// Panics if `path` is not a valid object file readable by the current user.
+/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
+/// parsed as a recognized object file.
+#[track_caller]
 pub fn any_symbol_contains(path: impl AsRef, substrings: &[&str]) -> bool {
     with_symbol_iter(path, |syms| {
         for sym in syms {

From 9734ebb9be2ad760385555e36bb0d065e726d6f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Mon, 20 Jan 2025 19:14:51 +0800
Subject: [PATCH 328/342] tests: port `symbol-mangling-hashed` to rmake.rs

- Use `object` based test logic instead of processing `nm`
  human-readable textual output.
- Try to expand test coverage to not be limited to only linux + x86_64.

Co-authored-by: binarycat 
---
 .../tidy/src/allowed_run_make_makefiles.txt   |   1 -
 .../run-make/symbol-mangling-hashed/Makefile  |  48 --------
 .../run-make/symbol-mangling-hashed/b_bin.rs  |   9 --
 .../symbol-mangling-hashed/b_dylib.rs         |   9 --
 .../symbol-mangling-hashed/default_bin.rs     |   9 ++
 .../symbol-mangling-hashed/default_dylib.rs   |   9 ++
 .../{a_dylib.rs => hashed_dylib.rs}           |   2 +-
 .../{a_rlib.rs => hashed_rlib.rs}             |   2 +-
 .../run-make/symbol-mangling-hashed/rmake.rs  | 108 ++++++++++++++++++
 9 files changed, 128 insertions(+), 69 deletions(-)
 delete mode 100644 tests/run-make/symbol-mangling-hashed/Makefile
 delete mode 100644 tests/run-make/symbol-mangling-hashed/b_bin.rs
 delete mode 100644 tests/run-make/symbol-mangling-hashed/b_dylib.rs
 create mode 100644 tests/run-make/symbol-mangling-hashed/default_bin.rs
 create mode 100644 tests/run-make/symbol-mangling-hashed/default_dylib.rs
 rename tests/run-make/symbol-mangling-hashed/{a_dylib.rs => hashed_dylib.rs} (74%)
 rename tests/run-make/symbol-mangling-hashed/{a_rlib.rs => hashed_rlib.rs} (74%)
 create mode 100644 tests/run-make/symbol-mangling-hashed/rmake.rs

diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index e75d3dc2147b..45b40b17ea37 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -1,2 +1 @@
 run-make/split-debuginfo/Makefile
-run-make/symbol-mangling-hashed/Makefile
diff --git a/tests/run-make/symbol-mangling-hashed/Makefile b/tests/run-make/symbol-mangling-hashed/Makefile
deleted file mode 100644
index c95036ead958..000000000000
--- a/tests/run-make/symbol-mangling-hashed/Makefile
+++ /dev/null
@@ -1,48 +0,0 @@
-include ../tools.mk
-
-# ignore-cross-compile
-# only-linux
-# only-x86_64
-
-NM=nm -D
-RLIB_NAME=liba_rlib.rlib
-DYLIB_NAME=liba_dylib.so
-SO_NAME=libb_dylib.so
-BIN_NAME=b_bin
-
-ifeq ($(UNAME),Darwin)
-NM=nm -gU
-RLIB_NAME=liba_rlib.rlib
-DYLIB_NAME=liba_dylib.dylib
-SO_NAME=libb_dylib.dylib
-BIN_NAME=b_bin
-endif
-
-ifdef IS_WINDOWS
-NM=nm -g
-RLIB_NAME=liba_rlib.dll.a
-DYLIB_NAME=liba_dylib.dll
-SO_NAME=libb_dylib.dll
-BIN_NAME=b_bin.exe
-endif
-
-all:
-	$(RUSTC) -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=hashed -C metadata=foo a_dylib.rs
-	$(RUSTC) -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=hashed -C metadata=bar a_rlib.rs
-	$(RUSTC) -C prefer-dynamic -L $(TMPDIR) b_dylib.rs
-	$(RUSTC) -C prefer-dynamic -L $(TMPDIR) b_bin.rs
-
-    # Check hashed symbol name
-
-	[ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep -c hello)" -eq "0" ]
-	[ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep _RNxC7a_dylib | grep -c ' T ')" -eq "2" ]
-
-	[ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep b_dylib | grep -c hello)" -eq "1" ]
-	[ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC6a_rlib | grep -c ' T ')" -eq "2" ]
-	[ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC7a_dylib | grep -c ' U ')" -eq "1" ]
-
-	[ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep _RNxC6a_rlib | grep -c ' U ')" -eq "1" ]
-	[ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep _RNxC7a_dylib | grep -c ' U ')" -eq "1" ]
-	[ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep b_dylib | grep hello | grep -c ' U ')" -eq "1" ]
-
-	$(call RUN,$(BIN_NAME))
diff --git a/tests/run-make/symbol-mangling-hashed/b_bin.rs b/tests/run-make/symbol-mangling-hashed/b_bin.rs
deleted file mode 100644
index 8ee7fecda62a..000000000000
--- a/tests/run-make/symbol-mangling-hashed/b_bin.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-extern crate a_dylib;
-extern crate a_rlib;
-extern crate b_dylib;
-
-fn main() {
-    a_rlib::hello();
-    a_dylib::hello();
-    b_dylib::hello();
-}
diff --git a/tests/run-make/symbol-mangling-hashed/b_dylib.rs b/tests/run-make/symbol-mangling-hashed/b_dylib.rs
deleted file mode 100644
index 3252c9c75c2a..000000000000
--- a/tests/run-make/symbol-mangling-hashed/b_dylib.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#![crate_type = "dylib"]
-
-extern crate a_dylib;
-extern crate a_rlib;
-
-pub fn hello() {
-    a_rlib::hello();
-    a_dylib::hello();
-}
diff --git a/tests/run-make/symbol-mangling-hashed/default_bin.rs b/tests/run-make/symbol-mangling-hashed/default_bin.rs
new file mode 100644
index 000000000000..387596705c36
--- /dev/null
+++ b/tests/run-make/symbol-mangling-hashed/default_bin.rs
@@ -0,0 +1,9 @@
+extern crate default_dylib;
+extern crate hashed_dylib;
+extern crate hashed_rlib;
+
+fn main() {
+    hashed_rlib::hrhello();
+    hashed_dylib::hdhello();
+    default_dylib::ddhello();
+}
diff --git a/tests/run-make/symbol-mangling-hashed/default_dylib.rs b/tests/run-make/symbol-mangling-hashed/default_dylib.rs
new file mode 100644
index 000000000000..986d1c7b74d2
--- /dev/null
+++ b/tests/run-make/symbol-mangling-hashed/default_dylib.rs
@@ -0,0 +1,9 @@
+#![crate_type = "dylib"]
+
+extern crate hashed_dylib;
+extern crate hashed_rlib;
+
+pub fn ddhello() {
+    hashed_rlib::hrhello();
+    hashed_dylib::hdhello();
+}
diff --git a/tests/run-make/symbol-mangling-hashed/a_dylib.rs b/tests/run-make/symbol-mangling-hashed/hashed_dylib.rs
similarity index 74%
rename from tests/run-make/symbol-mangling-hashed/a_dylib.rs
rename to tests/run-make/symbol-mangling-hashed/hashed_dylib.rs
index 49d65b72cacc..fbb7cba43e05 100644
--- a/tests/run-make/symbol-mangling-hashed/a_dylib.rs
+++ b/tests/run-make/symbol-mangling-hashed/hashed_dylib.rs
@@ -1,4 +1,4 @@
 #![crate_type = "dylib"]
-pub fn hello() {
+pub fn hdhello() {
     println!("hello dylib");
 }
diff --git a/tests/run-make/symbol-mangling-hashed/a_rlib.rs b/tests/run-make/symbol-mangling-hashed/hashed_rlib.rs
similarity index 74%
rename from tests/run-make/symbol-mangling-hashed/a_rlib.rs
rename to tests/run-make/symbol-mangling-hashed/hashed_rlib.rs
index 71e44ccc2007..048e67784d23 100644
--- a/tests/run-make/symbol-mangling-hashed/a_rlib.rs
+++ b/tests/run-make/symbol-mangling-hashed/hashed_rlib.rs
@@ -1,5 +1,5 @@
 #![crate_type = "rlib"]
 
-pub fn hello() {
+pub fn hrhello() {
     println!("hello rlib");
 }
diff --git a/tests/run-make/symbol-mangling-hashed/rmake.rs b/tests/run-make/symbol-mangling-hashed/rmake.rs
new file mode 100644
index 000000000000..136e6b9fa3a9
--- /dev/null
+++ b/tests/run-make/symbol-mangling-hashed/rmake.rs
@@ -0,0 +1,108 @@
+// ignore-tidy-linelength
+//! Basic smoke test for the unstable option `-C symbol_mangling_version=hashed` which aims to
+//! replace full symbol mangling names based on hash digests to shorten symbol name lengths in
+//! dylibs for space savings.
+//!
+//! # References
+//!
+//! - MCP #705: Provide option to shorten symbol names by replacing them with a digest:
+//!   .
+//! - Implementation PR: .
+//! - PE format: .
+
+//@ ignore-cross-compile
+
+#![deny(warnings)]
+
+use run_make_support::symbols::exported_dynamic_symbol_names;
+use run_make_support::{bin_name, cwd, dynamic_lib_name, is_darwin, object, rfs, run, rustc};
+
+macro_rules! adjust_symbol_prefix {
+    ($name:literal) => {
+        if is_darwin() { concat!("_", $name) } else { $name }
+    };
+}
+
+fn main() {
+    rustc()
+        .input("hashed_dylib.rs")
+        .prefer_dynamic()
+        .arg("-Zunstable-options")
+        .symbol_mangling_version("hashed")
+        .metadata("foo")
+        .run();
+
+    rustc()
+        .input("hashed_rlib.rs")
+        .prefer_dynamic()
+        .arg("-Zunstable-options")
+        .symbol_mangling_version("hashed")
+        .metadata("bar")
+        .run();
+
+    rustc().input("default_dylib.rs").library_search_path(cwd()).prefer_dynamic().run();
+    rustc().input("default_bin.rs").library_search_path(cwd()).prefer_dynamic().run();
+
+    {
+        // Check hashed symbol name
+
+        let dylib_filename = dynamic_lib_name("hashed_dylib");
+        println!("checking dylib `{dylib_filename}`");
+
+        let dylib_blob = rfs::read(&dylib_filename);
+        let dylib_file = object::File::parse(&*dylib_blob)
+            .unwrap_or_else(|e| panic!("failed to parse `{dylib_filename}`: {e}"));
+
+        let dynamic_symbols = exported_dynamic_symbol_names(&dylib_file);
+
+        if dynamic_symbols.iter().filter(|sym| sym.contains("hdhello")).count() != 0 {
+            eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+            panic!("expected no occurrence of `hdhello`");
+        }
+
+        let expected_prefix = adjust_symbol_prefix!("_RNxC12hashed_dylib");
+        if dynamic_symbols.iter().filter(|sym| sym.starts_with(expected_prefix)).count() != 2 {
+            eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+            panic!("expected two dynamic symbols starting with `{expected_prefix}`");
+        }
+    }
+
+    {
+        let dylib_filename = dynamic_lib_name("default_dylib");
+        println!("checking so `{dylib_filename}`");
+
+        let dylib_blob = rfs::read(&dylib_filename);
+        let dylib_file = object::File::parse(&*dylib_blob)
+            .unwrap_or_else(|e| panic!("failed to parse `{dylib_filename}`: {e}"));
+
+        let dynamic_symbols = exported_dynamic_symbol_names(&dylib_file);
+
+        if dynamic_symbols
+            .iter()
+            .filter(|sym| sym.contains("default_dylib") && sym.contains("ddhello"))
+            .count()
+            != 1
+        {
+            eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+            panic!("expected one occurrence of mangled `ddhello`");
+        }
+
+        let expected_rlib_prefix = adjust_symbol_prefix!("_RNxC11hashed_rlib");
+        if dynamic_symbols.iter().filter(|sym| sym.starts_with(expected_rlib_prefix)).count() != 2 {
+            eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+            panic!("expected two exported symbols starting with `{expected_rlib_prefix}`");
+        }
+
+        let expected_dylib_prefix = adjust_symbol_prefix!("_RNxC12hashed_dylib");
+        if dynamic_symbols.iter().any(|sym| sym.starts_with("_RNxC12hashed_dylib")) {
+            eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+            panic!("did not expect any symbols starting with `{expected_dylib_prefix}`");
+        }
+    }
+
+    // Check that the final binary can be run.
+    {
+        let bin_filename = bin_name("default_bin");
+        run(&bin_filename);
+    }
+}

From 3f5e21858177714865b67a5f064fdfef07a5cd56 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 09:42:41 +1100
Subject: [PATCH 329/342] Give a better explanation for having `bug_fmt` and
 `span_bug_fmt`.

---
 compiler/rustc_middle/src/util/bug.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs
index b99336c2c85b..7dda68b8393a 100644
--- a/compiler/rustc_middle/src/util/bug.rs
+++ b/compiler/rustc_middle/src/util/bug.rs
@@ -1,4 +1,4 @@
-// These functions are used by macro expansion for bug! and span_bug!
+// These functions are used by macro expansion for `bug!` and `span_bug!`.
 
 use std::fmt;
 use std::panic::{Location, panic_any};
@@ -8,15 +8,15 @@ use rustc_span::Span;
 
 use crate::ty::{TyCtxt, tls};
 
+// This wrapper makes for more compact code at callsites than calling `opt_span_buf_fmt` directly.
 #[cold]
 #[inline(never)]
 #[track_caller]
 pub fn bug_fmt(args: fmt::Arguments<'_>) -> ! {
-    // this wrapper mostly exists so I don't have to write a fully
-    // qualified path of None:: inside the bug!() macro definition
     opt_span_bug_fmt(None::, args, Location::caller());
 }
 
+// This wrapper makes for more compact code at callsites than calling `opt_span_buf_fmt` directly.
 #[cold]
 #[inline(never)]
 #[track_caller]

From 140817380c0ef0eb795042551788f0679aeab545 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 09:55:55 +1100
Subject: [PATCH 330/342] Move `find_self_call`.

It's a function that does stuff with MIR and yet it weirdly has its own
module in `rustc_middle::util`. This commit moves it into
`rustc_middle::mir`, a more sensible home.
---
 .../src/diagnostics/conflict_errors.rs        |  2 +-
 .../rustc_borrowck/src/diagnostics/mod.rs     | 11 ++---
 compiler/rustc_middle/src/mir/mod.rs          | 43 ++++++++++++++++-
 .../rustc_middle/src/util/find_self_call.rs   | 47 -------------------
 compiler/rustc_middle/src/util/mod.rs         |  3 --
 .../src/check_const_item_mutation.rs          |  2 +-
 6 files changed, 48 insertions(+), 60 deletions(-)
 delete mode 100644 compiler/rustc_middle/src/util/find_self_call.rs

diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 07dcbba019ad..0118b72962de 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -3777,7 +3777,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let tcx = self.infcx.tcx;
         if let Some(Terminator { kind: TerminatorKind::Call { call_source, fn_span, .. }, .. }) =
             &self.body[loan.reserve_location.block].terminator
-            && let Some((method_did, method_args)) = rustc_middle::util::find_self_call(
+            && let Some((method_did, method_args)) = mir::find_self_call(
                 tcx,
                 self.body,
                 loan.assigned_place.local,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index bd6f77156ca9..5fe8eef34600 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -17,7 +17,7 @@ use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
     AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
     LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement,
-    StatementKind, Terminator, TerminatorKind,
+    StatementKind, Terminator, TerminatorKind, find_self_call,
 };
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -1016,12 +1016,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
         }) = &self.body[location.block].terminator
         {
-            let Some((method_did, method_args)) = rustc_middle::util::find_self_call(
-                self.infcx.tcx,
-                self.body,
-                target_temp,
-                location.block,
-            ) else {
+            let Some((method_did, method_args)) =
+                find_self_call(self.infcx.tcx, self.body, target_temp, location.block)
+            else {
                 return normal_ret;
             };
 
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 0f3fca434eec..cfb78e5dddff 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -27,7 +27,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisit
 use rustc_serialize::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span, Symbol};
-use tracing::trace;
+use tracing::{debug, trace};
 
 pub use self::query::*;
 use self::visit::TyContext;
@@ -1796,6 +1796,47 @@ impl DefLocation {
     }
 }
 
+/// Checks if the specified `local` is used as the `self` parameter of a method call
+/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
+/// returned.
+pub fn find_self_call<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    local: Local,
+    block: BasicBlock,
+) -> Option<(DefId, GenericArgsRef<'tcx>)> {
+    debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator);
+    if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
+        &body[block].terminator
+        && let Operand::Constant(box ConstOperand { const_, .. }) = func
+        && let ty::FnDef(def_id, fn_args) = *const_.ty().kind()
+        && let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
+            tcx.opt_associated_item(def_id)
+        && let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] =
+            **args
+    {
+        if self_place.as_local() == Some(local) {
+            return Some((def_id, fn_args));
+        }
+
+        // Handle the case where `self_place` gets reborrowed.
+        // This happens when the receiver is `&T`.
+        for stmt in &body[block].statements {
+            if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind
+                && let Some(reborrow_local) = place.as_local()
+                && self_place.as_local() == Some(reborrow_local)
+                && let Rvalue::Ref(_, _, deref_place) = rvalue
+                && let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } =
+                    deref_place.as_ref()
+                && deref_local == local
+            {
+                return Some((def_id, fn_args));
+            }
+        }
+    }
+    None
+}
+
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs
deleted file mode 100644
index 0fdd35207386..000000000000
--- a/compiler/rustc_middle/src/util/find_self_call.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use rustc_span::def_id::DefId;
-use rustc_span::source_map::Spanned;
-use tracing::debug;
-
-use crate::mir::*;
-use crate::ty::{self, GenericArgsRef, TyCtxt};
-
-/// Checks if the specified `local` is used as the `self` parameter of a method call
-/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
-/// returned.
-pub fn find_self_call<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
-    local: Local,
-    block: BasicBlock,
-) -> Option<(DefId, GenericArgsRef<'tcx>)> {
-    debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator);
-    if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
-        &body[block].terminator
-        && let Operand::Constant(box ConstOperand { const_, .. }) = func
-        && let ty::FnDef(def_id, fn_args) = *const_.ty().kind()
-        && let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
-            tcx.opt_associated_item(def_id)
-        && let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] =
-            **args
-    {
-        if self_place.as_local() == Some(local) {
-            return Some((def_id, fn_args));
-        }
-
-        // Handle the case where `self_place` gets reborrowed.
-        // This happens when the receiver is `&T`.
-        for stmt in &body[block].statements {
-            if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind
-                && let Some(reborrow_local) = place.as_local()
-                && self_place.as_local() == Some(reborrow_local)
-                && let Rvalue::Ref(_, _, deref_place) = rvalue
-                && let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } =
-                    deref_place.as_ref()
-                && deref_local == local
-            {
-                return Some((def_id, fn_args));
-            }
-        }
-    }
-    None
-}
diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs
index 097a868191c1..3fe8887437d2 100644
--- a/compiler/rustc_middle/src/util/mod.rs
+++ b/compiler/rustc_middle/src/util/mod.rs
@@ -1,8 +1,5 @@
 pub mod bug;
 pub mod common;
-pub mod find_self_call;
-
-pub use find_self_call::find_self_call;
 
 #[derive(Default, Copy, Clone)]
 pub struct Providers {
diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
index 7968b666dff2..3affe4abbfad 100644
--- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
+++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
@@ -133,7 +133,7 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
                 // the `self` parameter of a method call (as the terminator of our current
                 // BasicBlock). If so, we emit a more specific lint.
                 let method_did = self.target_local.and_then(|target_local| {
-                    rustc_middle::util::find_self_call(self.tcx, self.body, target_local, loc.block)
+                    find_self_call(self.tcx, self.body, target_local, loc.block)
                 });
                 let lint_loc =
                     if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };

From 4ced93ed35ca7c88d618286488a5cf960049de5c Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 10:09:18 +1100
Subject: [PATCH 331/342] Don't export the `Trivial*` macros.

They're only used within the crate.
---
 compiler/rustc_middle/src/macros.rs     | 3 ---
 compiler/rustc_middle/src/traits/mod.rs | 2 +-
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs
index 300dfb95dcb9..b3064d8fe25c 100644
--- a/compiler/rustc_middle/src/macros.rs
+++ b/compiler/rustc_middle/src/macros.rs
@@ -41,7 +41,6 @@ macro_rules! span_bug {
 // When possible, use one of these (relatively) convenient macros to write
 // the impls for you.
 
-#[macro_export]
 macro_rules! TrivialLiftImpls {
     ($($ty:ty),+ $(,)?) => {
         $(
@@ -57,7 +56,6 @@ macro_rules! TrivialLiftImpls {
 
 /// Used for types that are `Copy` and which **do not care about arena
 /// allocated data** (i.e., don't need to be folded).
-#[macro_export]
 macro_rules! TrivialTypeTraversalImpls {
     ($($ty:ty),+ $(,)?) => {
         $(
@@ -92,7 +90,6 @@ macro_rules! TrivialTypeTraversalImpls {
     };
 }
 
-#[macro_export]
 macro_rules! TrivialTypeTraversalAndLiftImpls {
     ($($t:tt)*) => {
         TrivialTypeTraversalImpls! { $($t)* }
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 8a9110f842a9..28a6eba75aa5 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -428,7 +428,7 @@ pub enum IsConstable {
     Ctor,
 }
 
-crate::TrivialTypeTraversalAndLiftImpls! {
+TrivialTypeTraversalAndLiftImpls! {
     IsConstable,
 }
 

From 0c470910061c9ad379b1cbf8a74315bc1a9b3c74 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Fri, 31 Jan 2025 10:58:33 +1100
Subject: [PATCH 332/342] Overhaul `to_readable_str`.

It's a function that prints numbers with underscores inserted for
readability (e.g. "1_234_567"), used by `-Zmeta-stats` and
`-Zinput-stats`. It's the only thing in `rustc_middle::util::common`,
which is a bizarre location for it.

This commit:
- moves it to `rustc_data_structures`, a more logical crate for it;
- puts it in a module `thousands`, like the similar crates.io crate;
- renames it `format_with_underscores`, which is a clearer name;
- rewrites it to be more concise;
- slightly improves the testing.
---
 compiler/rustc_data_structures/src/lib.rs     |  1 +
 .../src/thousands/mod.rs                      | 16 ++++++++++++++
 .../src/thousands/tests.rs                    | 14 ++++++++++++
 compiler/rustc_metadata/src/rmeta/encoder.rs  |  6 ++---
 compiler/rustc_middle/src/util/common.rs      | 22 -------------------
 .../rustc_middle/src/util/common/tests.rs     | 14 ------------
 compiler/rustc_middle/src/util/mod.rs         |  1 -
 compiler/rustc_passes/src/input_stats.rs      | 16 +++++++-------
 8 files changed, 42 insertions(+), 48 deletions(-)
 create mode 100644 compiler/rustc_data_structures/src/thousands/mod.rs
 create mode 100644 compiler/rustc_data_structures/src/thousands/tests.rs
 delete mode 100644 compiler/rustc_middle/src/util/common.rs
 delete mode 100644 compiler/rustc_middle/src/util/common/tests.rs

diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 65d586124b33..6ef73debadd5 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -76,6 +76,7 @@ pub mod sync;
 pub mod tagged_ptr;
 pub mod temp_dir;
 pub mod thinvec;
+pub mod thousands;
 pub mod transitive_relation;
 pub mod unhash;
 pub mod unord;
diff --git a/compiler/rustc_data_structures/src/thousands/mod.rs b/compiler/rustc_data_structures/src/thousands/mod.rs
new file mode 100644
index 000000000000..e7ab7ec2932d
--- /dev/null
+++ b/compiler/rustc_data_structures/src/thousands/mod.rs
@@ -0,0 +1,16 @@
+//! This is an extremely bare-bones alternative to the `thousands` crate on
+//! crates.io, for printing large numbers in a readable fashion.
+
+#[cfg(test)]
+mod tests;
+
+// Converts the number to a string, with underscores as the thousands separator.
+pub fn format_with_underscores(n: usize) -> String {
+    let mut s = n.to_string();
+    let mut i = s.len();
+    while i > 3 {
+        i -= 3;
+        s.insert(i, '_');
+    }
+    s
+}
diff --git a/compiler/rustc_data_structures/src/thousands/tests.rs b/compiler/rustc_data_structures/src/thousands/tests.rs
new file mode 100644
index 000000000000..906605d9a935
--- /dev/null
+++ b/compiler/rustc_data_structures/src/thousands/tests.rs
@@ -0,0 +1,14 @@
+use super::*;
+
+#[test]
+fn test_format_with_underscores() {
+    assert_eq!("0", format_with_underscores(0));
+    assert_eq!("1", format_with_underscores(1));
+    assert_eq!("99", format_with_underscores(99));
+    assert_eq!("345", format_with_underscores(345));
+    assert_eq!("1_000", format_with_underscores(1_000));
+    assert_eq!("12_001", format_with_underscores(12_001));
+    assert_eq!("999_999", format_with_underscores(999_999));
+    assert_eq!("1_000_000", format_with_underscores(1_000_000));
+    assert_eq!("12_345_678", format_with_underscores(12_345_678));
+}
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 904409dd777e..43372154b2ce 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::memmap::{Mmap, MmapMut};
 use rustc_data_structures::sync::{Lrc, join, par_for_each_in};
 use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_data_structures::thousands::format_with_underscores;
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId, LocalDefIdSet};
@@ -22,7 +23,6 @@ use rustc_middle::traits::specialization_graph;
 use rustc_middle::ty::codec::TyEncoder;
 use rustc_middle::ty::fast_reject::{self, TreatParams};
 use rustc_middle::ty::{AssocItemContainer, SymbolName};
-use rustc_middle::util::common::to_readable_str;
 use rustc_middle::{bug, span_bug};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque};
 use rustc_session::config::{CrateType, OptLevel};
@@ -782,7 +782,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     "{} {:<23}{:>10} ({:4.1}%)",
                     prefix,
                     label,
-                    to_readable_str(size),
+                    format_with_underscores(size),
                     perc(size)
                 );
             }
@@ -791,7 +791,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 "{} {:<23}{:>10} (of which {:.1}% are zero bytes)",
                 prefix,
                 "Total",
-                to_readable_str(total_bytes),
+                format_with_underscores(total_bytes),
                 perc(zero_bytes)
             );
             eprintln!("{prefix}");
diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs
deleted file mode 100644
index 223b2b3bfe4d..000000000000
--- a/compiler/rustc_middle/src/util/common.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-#[cfg(test)]
-mod tests;
-
-pub fn to_readable_str(mut val: usize) -> String {
-    let mut groups = vec![];
-    loop {
-        let group = val % 1000;
-
-        val /= 1000;
-
-        if val == 0 {
-            groups.push(group.to_string());
-            break;
-        } else {
-            groups.push(format!("{group:03}"));
-        }
-    }
-
-    groups.reverse();
-
-    groups.join("_")
-}
diff --git a/compiler/rustc_middle/src/util/common/tests.rs b/compiler/rustc_middle/src/util/common/tests.rs
deleted file mode 100644
index 9a9fb203c625..000000000000
--- a/compiler/rustc_middle/src/util/common/tests.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use super::*;
-
-#[test]
-fn test_to_readable_str() {
-    assert_eq!("0", to_readable_str(0));
-    assert_eq!("1", to_readable_str(1));
-    assert_eq!("99", to_readable_str(99));
-    assert_eq!("999", to_readable_str(999));
-    assert_eq!("1_000", to_readable_str(1_000));
-    assert_eq!("1_001", to_readable_str(1_001));
-    assert_eq!("999_999", to_readable_str(999_999));
-    assert_eq!("1_000_000", to_readable_str(1_000_000));
-    assert_eq!("1_234_567", to_readable_str(1_234_567));
-}
diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs
index 3fe8887437d2..8c875007b7fb 100644
--- a/compiler/rustc_middle/src/util/mod.rs
+++ b/compiler/rustc_middle/src/util/mod.rs
@@ -1,5 +1,4 @@
 pub mod bug;
-pub mod common;
 
 #[derive(Default, Copy, Clone)]
 pub struct Providers {
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index 75b62a40ff9f..6d9c70177a4d 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -5,10 +5,10 @@
 use rustc_ast::visit::BoundKind;
 use rustc_ast::{self as ast, NodeId, visit as ast_visit};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::thousands::format_with_underscores;
 use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::TyCtxt;
-use rustc_middle::util::common::to_readable_str;
 use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
 
@@ -144,10 +144,10 @@ impl<'k> StatCollector<'k> {
                 "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
                 prefix,
                 label,
-                to_readable_str(size),
+                format_with_underscores(size),
                 percent(size, total_size),
-                to_readable_str(node.stats.count),
-                to_readable_str(node.stats.size)
+                format_with_underscores(node.stats.count),
+                format_with_underscores(node.stats.size)
             );
             if !node.subnodes.is_empty() {
                 // We will soon sort, so the initial order does not matter.
@@ -163,9 +163,9 @@ impl<'k> StatCollector<'k> {
                         "{} - {:<18}{:>10} ({:4.1}%){:>14}",
                         prefix,
                         label,
-                        to_readable_str(size),
+                        format_with_underscores(size),
                         percent(size, total_size),
-                        to_readable_str(subnode.count),
+                        format_with_underscores(subnode.count),
                     );
                 }
             }
@@ -175,8 +175,8 @@ impl<'k> StatCollector<'k> {
             "{} {:<18}{:>10}        {:>14}",
             prefix,
             "Total",
-            to_readable_str(total_size),
-            to_readable_str(total_count),
+            format_with_underscores(total_size),
+            format_with_underscores(total_count),
         );
         eprintln!("{prefix}");
     }

From 780cd71d2fc5fba10a29eea50c8ee6db22127f40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Fri, 31 Jan 2025 14:46:13 +0800
Subject: [PATCH 333/342] triagebot: remove myself from vacation

---
 triagebot.toml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/triagebot.toml b/triagebot.toml
index 29c84e19c56a..49cf1213987a 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1025,7 +1025,6 @@ users_on_vacation = [
     "nnethercote",
     "workingjubilee",
     "kobzol",
-    "jieyouxu",
 ]
 
 [[assign.warn_non_default_branch.exceptions]]

From 7df38d94a1de6194dc6ecf474e62cff94fbadd22 Mon Sep 17 00:00:00 2001
From: Mads Marquart 
Date: Fri, 24 Jan 2025 14:44:51 +0100
Subject: [PATCH 334/342] Recommend adding target spec first, then std support
 later

Co-Authored-By: Jieyou Xu 
---
 src/doc/rustc/src/target-tier-policy.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md
index 813c052d6dbf..a0acfdf0e4ab 100644
--- a/src/doc/rustc/src/target-tier-policy.md
+++ b/src/doc/rustc/src/target-tier-policy.md
@@ -125,6 +125,13 @@ To propose addition of a new target, open a pull request on [`rust-lang/rust`]:
 See also the documentation in the `rustc-dev-guide` on [adding a new target to
 `rustc`][rustc_dev_guide_add_target].
 
+Note that adding a new target that wants to support `std` would transitively
+require `cc` and `libc` support. However, these would like to know about the
+target from `rustc` as well. To break this cycle, you are strongly encouraged
+to add a _minimal_ `#![no_core]` target spec first to teach `rustc` about the
+target's existence, and add `std` support as a follow-up once you've added
+support for the target in `cc` and `libc`.
+
 [tier3example]: https://github.com/rust-lang/rust/pull/94872
 [platform_template]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/platform-support/TEMPLATE.md
 [summary]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/SUMMARY.md

From d98270b07a04178b019d8317d146eb3dd1afface Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Fri, 31 Jan 2025 11:53:32 +0100
Subject: [PATCH 335/342] miri: make float min/max non-deterministic

---
 .../src/interpret/intrinsics.rs               | 16 +++++++++--
 .../rustc_const_eval/src/interpret/machine.rs |  6 ++++
 src/tools/miri/src/machine.rs                 | 13 ++++++---
 src/tools/miri/src/operator.rs                |  7 +++++
 src/tools/miri/tests/pass/float.rs            | 28 +++++++++++++++++++
 5 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 0664a882c1d5..9f5f2533e085 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -747,7 +747,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     {
         let a: F = self.read_scalar(&args[0])?.to_float()?;
         let b: F = self.read_scalar(&args[1])?.to_float()?;
-        let res = self.adjust_nan(a.min(b), &[a, b]);
+        let res = if a == b {
+            // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
+            // Let the machine decide which one to return.
+            M::equal_float_min_max(self, a, b)
+        } else {
+            self.adjust_nan(a.min(b), &[a, b])
+        };
         self.write_scalar(res, dest)?;
         interp_ok(())
     }
@@ -762,7 +768,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     {
         let a: F = self.read_scalar(&args[0])?.to_float()?;
         let b: F = self.read_scalar(&args[1])?.to_float()?;
-        let res = self.adjust_nan(a.max(b), &[a, b]);
+        let res = if a == b {
+            // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
+            // Let the machine decide which one to return.
+            M::equal_float_min_max(self, a, b)
+        } else {
+            self.adjust_nan(a.max(b), &[a, b])
+        };
         self.write_scalar(res, dest)?;
         interp_ok(())
     }
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 36e5a2ff750a..8f6b15b8df01 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -278,6 +278,12 @@ pub trait Machine<'tcx>: Sized {
         F2::NAN
     }
 
+    /// Determines the result of `min`/`max` on floats when the arguments are equal.
+    fn equal_float_min_max(_ecx: &InterpCx<'tcx, Self>, a: F, _b: F) -> F {
+        // By default, we pick the left argument.
+        a
+    }
+
     /// Called before a basic block terminator is executed.
     #[inline]
     fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 845ba484326f..3727b5f4cae4 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -11,6 +11,7 @@ use std::{fmt, process};
 use rand::rngs::StdRng;
 use rand::{Rng, SeedableRng};
 use rustc_abi::{Align, ExternAbi, Size};
+use rustc_apfloat::{Float, FloatConvert};
 use rustc_attr_parsing::InlineAttr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 #[allow(unused)]
@@ -1129,20 +1130,24 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
     }
 
     #[inline(always)]
-    fn generate_nan<
-        F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert,
-        F2: rustc_apfloat::Float,
-    >(
+    fn generate_nan, F2: Float>(
         ecx: &InterpCx<'tcx, Self>,
         inputs: &[F1],
     ) -> F2 {
         ecx.generate_nan(inputs)
     }
 
+    #[inline(always)]
+    fn equal_float_min_max(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
+        ecx.equal_float_min_max(a, b)
+    }
+
+    #[inline(always)]
     fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
         interp_ok(ecx.tcx.sess.ub_checks())
     }
 
+    #[inline(always)]
     fn thread_local_static_pointer(
         ecx: &mut MiriInterpCx<'tcx>,
         def_id: DefId,
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index 0017a3991b53..43c628d66d59 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -115,4 +115,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             nan
         }
     }
+
+    fn equal_float_min_max(&self, a: F, b: F) -> F {
+        let this = self.eval_context_ref();
+        // Return one side non-deterministically.
+        let mut rand = this.machine.rng.borrow_mut();
+        if rand.gen() { a } else { b }
+    }
 }
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 4de315e35897..2f4f64b1aa80 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -31,6 +31,7 @@ fn main() {
     test_fast();
     test_algebraic();
     test_fmuladd();
+    test_min_max_nondet();
 }
 
 trait Float: Copy + PartialEq + Debug {
@@ -1211,3 +1212,30 @@ fn test_fmuladd() {
     test_operations_f32(0.1, 0.2, 0.3);
     test_operations_f64(1.1, 1.2, 1.3);
 }
+
+/// `min` and `max` on equal arguments are non-deterministic.
+fn test_min_max_nondet() {
+    /// Ensure that if we call the closure often enough, we see both `true` and `false.`
+    #[track_caller]
+    fn ensure_both(f: impl Fn() -> bool) {
+        let rounds = 16;
+        let first = f();
+        for _ in 1..rounds {
+            if f() != first {
+                // We saw two different values!
+                return;
+            }
+        }
+        // We saw the same thing N times.
+        panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
+    }
+
+    ensure_both(|| f16::min(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f16::max(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f32::min(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f32::max(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f64::min(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f64::max(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
+    ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
+}

From b151b513ba2b65c7506ec1a80f2712bbd09154d1 Mon Sep 17 00:00:00 2001
From: Bastian Kersting 
Date: Tue, 17 Dec 2024 13:00:22 +0000
Subject: [PATCH 336/342] Insert null checks for pointer dereferences when
 debug assertions are enabled

Similar to how the alignment is already checked, this adds a check
for null pointer dereferences in debug mode. It is implemented similarly
to the alignment check as a MirPass.

This is related to a 2025H1 project goal for better UB checks in debug
mode: https://github.com/rust-lang/rust-project-goals/pull/177.
---
 compiler/rustc_codegen_cranelift/src/base.rs  |  10 ++
 compiler/rustc_codegen_ssa/src/mir/block.rs   |   5 +
 .../src/const_eval/machine.rs                 |   1 +
 compiler/rustc_hir/src/lang_items.rs          |   1 +
 compiler/rustc_middle/messages.ftl            |   3 +
 compiler/rustc_middle/src/mir/syntax.rs       |   1 +
 compiler/rustc_middle/src/mir/terminator.rs   |   6 +-
 compiler/rustc_middle/src/mir/visit.rs        |   2 +-
 .../rustc_mir_transform/src/check_null.rs     | 110 ++++++++++++++++++
 .../rustc_mir_transform/src/check_pointers.rs |   3 +-
 compiler/rustc_mir_transform/src/lib.rs       |   2 +
 compiler/rustc_monomorphize/src/collector.rs  |   3 +
 .../rustc_smir/src/rustc_smir/convert/mir.rs  |   3 +
 compiler/rustc_span/src/symbol.rs             |   1 +
 compiler/stable_mir/src/mir/body.rs           |   2 +
 compiler/stable_mir/src/mir/pretty.rs         |   3 +
 compiler/stable_mir/src/mir/visit.rs          |   5 +-
 library/core/src/panicking.rs                 |  16 +++
 src/tools/clippy/src/driver.rs                |   2 +
 src/tools/miri/src/lib.rs                     |   2 +-
 .../crates/hir-def/src/lang_item.rs           |   1 +
 .../crates/intern/src/symbol/symbols.rs       |   1 +
 tests/mir-opt/null_check_references.rs        |  16 +++
 tests/ui/abi/segfault-no-out-of-stack.rs      |   1 +
 tests/ui/mir/null/addrof_null.rs              |  14 +++
 tests/ui/mir/null/borrowed_mut_null.rs        |   8 ++
 tests/ui/mir/null/borrowed_null.rs            |   8 ++
 tests/ui/mir/null/null_lhs.rs                 |  10 ++
 tests/ui/mir/null/null_rhs.rs                 |  10 ++
 tests/ui/mir/null/place_without_read.rs       |  10 ++
 tests/ui/mir/null/two_pointers.rs             |  12 ++
 tests/ui/mir/null/zero_sized_access.rs        |  15 +++
 32 files changed, 281 insertions(+), 6 deletions(-)
 create mode 100644 compiler/rustc_mir_transform/src/check_null.rs
 create mode 100644 tests/mir-opt/null_check_references.rs
 create mode 100644 tests/ui/mir/null/addrof_null.rs
 create mode 100644 tests/ui/mir/null/borrowed_mut_null.rs
 create mode 100644 tests/ui/mir/null/borrowed_null.rs
 create mode 100644 tests/ui/mir/null/null_lhs.rs
 create mode 100644 tests/ui/mir/null/null_rhs.rs
 create mode 100644 tests/ui/mir/null/place_without_read.rs
 create mode 100644 tests/ui/mir/null/two_pointers.rs
 create mode 100644 tests/ui/mir/null/zero_sized_access.rs

diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 34066eb83fc0..c7c9c6236d1d 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -417,6 +417,16 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
                             Some(source_info.span),
                         );
                     }
+                    AssertKind::NullPointerDereference => {
+                        let location = fx.get_caller_location(source_info).load_scalar(fx);
+
+                        codegen_panic_inner(
+                            fx,
+                            rustc_hir::LangItem::PanicNullPointerDereference,
+                            &[location],
+                            Some(source_info.span),
+                        )
+                    }
                     _ => {
                         let location = fx.get_caller_location(source_info).load_scalar(fx);
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index b0a1dedd646b..4be363ca9a2b 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -713,6 +713,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 // and `#[track_caller]` adds an implicit third argument.
                 (LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
             }
+            AssertKind::NullPointerDereference => {
+                // It's `fn panic_null_pointer_dereference()`,
+                // `#[track_caller]` adds an implicit argument.
+                (LangItem::PanicNullPointerDereference, vec![location])
+            }
             _ => {
                 // It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument.
                 (msg.panic_function(), vec![location])
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 6a339d695426..82e0a6e6666f 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -508,6 +508,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
                     found: eval_to_int(found)?,
                 }
             }
+            NullPointerDereference => NullPointerDereference,
         };
         Err(ConstEvalErrKind::AssertFailure(err)).into()
     }
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 02bc069fc5f2..d9759580e8fd 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -316,6 +316,7 @@ language_item_table! {
     PanicAsyncFnResumedPanic, sym::panic_const_async_fn_resumed_panic, panic_const_async_fn_resumed_panic, Target::Fn, GenericRequirement::None;
     PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None;
     PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None;
+    PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None;
     /// libstd panic entry point. Necessary for const eval to be able to catch it
     BeginPanic,              sym::begin_panic,         begin_panic_fn,             Target::Fn,             GenericRequirement::None;
 
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 8dc529b4d7a2..f74cea23c241 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -17,6 +17,9 @@ middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further itera
 middle_assert_misaligned_ptr_deref =
     misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
 
+middle_assert_null_ptr_deref =
+    null pointer dereference occurred
+
 middle_assert_op_overflow =
     attempt to compute `{$left} {$op} {$right}`, which would overflow
 
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 5868b64f6b5c..90a2f175bc99 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1076,6 +1076,7 @@ pub enum AssertKind {
     ResumedAfterReturn(CoroutineKind),
     ResumedAfterPanic(CoroutineKind),
     MisalignedPointerDereference { required: O, found: O },
+    NullPointerDereference,
 }
 
 #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index c04a8251fbc5..2fe116212eb5 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -206,6 +206,7 @@ impl AssertKind {
             ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
                 LangItem::PanicGenFnNonePanic
             }
+            NullPointerDereference => LangItem::PanicNullPointerDereference,
 
             BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
                 bug!("Unexpected AssertKind")
@@ -271,6 +272,7 @@ impl AssertKind {
                     "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
                 )
             }
+            NullPointerDereference => write!(f, "\"null pointer dereference occured\""),
             ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
                 write!(f, "\"coroutine resumed after completion\"")
             }
@@ -341,7 +343,7 @@ impl AssertKind {
             ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
                 middle_assert_coroutine_resume_after_panic
             }
-
+            NullPointerDereference => middle_assert_null_ptr_deref,
             MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
         }
     }
@@ -374,7 +376,7 @@ impl AssertKind {
                 add!("left", format!("{left:#?}"));
                 add!("right", format!("{right:#?}"));
             }
-            ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
+            ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {}
             MisalignedPointerDereference { required, found } => {
                 add!("required", format!("{required:#?}"));
                 add!("found", format!("{found:#?}"));
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 95de08ce9c87..6319a7e65b98 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -636,7 +636,7 @@ macro_rules! make_mir_visitor {
                     OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
                         self.visit_operand(op, location);
                     }
-                    ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
+                    ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {
                         // Nothing to visit
                     }
                     MisalignedPointerDereference { required, found } => {
diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs
new file mode 100644
index 000000000000..0b6c0ceaac19
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/check_null.rs
@@ -0,0 +1,110 @@
+use rustc_index::IndexVec;
+use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::mir::*;
+use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_session::Session;
+
+use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
+
+pub(super) struct CheckNull;
+
+impl<'tcx> crate::MirPass<'tcx> for CheckNull {
+    fn is_enabled(&self, sess: &Session) -> bool {
+        sess.ub_checks()
+    }
+
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        check_pointers(tcx, body, &[], insert_null_check, BorrowCheckMode::IncludeBorrows);
+    }
+
+    fn is_required(&self) -> bool {
+        true
+    }
+}
+
+fn insert_null_check<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    pointer: Place<'tcx>,
+    pointee_ty: Ty<'tcx>,
+    local_decls: &mut IndexVec>,
+    stmts: &mut Vec>,
+    source_info: SourceInfo,
+) -> PointerCheck<'tcx> {
+    // Cast the pointer to a *const ().
+    let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
+    let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
+    let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
+    stmts
+        .push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) });
+
+    // Transmute the pointer to a usize (equivalent to `ptr.addr()`).
+    let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize);
+    let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
+    stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });
+
+    // Get size of the pointee (zero-sized reads and writes are allowed).
+    let rvalue = Rvalue::NullaryOp(NullOp::SizeOf, pointee_ty);
+    let sizeof_pointee =
+        local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
+    stmts.push(Statement {
+        source_info,
+        kind: StatementKind::Assign(Box::new((sizeof_pointee, rvalue))),
+    });
+
+    // Check that the pointee is not a ZST.
+    let zero = Operand::Constant(Box::new(ConstOperand {
+        span: source_info.span,
+        user_ty: None,
+        const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize),
+    }));
+    let is_pointee_no_zst =
+        local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
+    stmts.push(Statement {
+        source_info,
+        kind: StatementKind::Assign(Box::new((
+            is_pointee_no_zst,
+            Rvalue::BinaryOp(BinOp::Ne, Box::new((Operand::Copy(sizeof_pointee), zero.clone()))),
+        ))),
+    });
+
+    // Check whether the pointer is null.
+    let is_null = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
+    stmts.push(Statement {
+        source_info,
+        kind: StatementKind::Assign(Box::new((
+            is_null,
+            Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(addr), zero))),
+        ))),
+    });
+
+    // We want to throw an exception if the pointer is null and doesn't point to a ZST.
+    let should_throw_exception =
+        local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
+    stmts.push(Statement {
+        source_info,
+        kind: StatementKind::Assign(Box::new((
+            should_throw_exception,
+            Rvalue::BinaryOp(
+                BinOp::BitAnd,
+                Box::new((Operand::Copy(is_null), Operand::Copy(is_pointee_no_zst))),
+            ),
+        ))),
+    });
+
+    // The final condition whether this pointer usage is ok or not.
+    let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
+    stmts.push(Statement {
+        source_info,
+        kind: StatementKind::Assign(Box::new((
+            is_ok,
+            Rvalue::UnaryOp(UnOp::Not, Operand::Copy(should_throw_exception)),
+        ))),
+    });
+
+    // Emit a PointerCheck that asserts on the condition and otherwise triggers
+    // a AssertKind::NullPointerDereference.
+    PointerCheck {
+        cond: Operand::Copy(is_ok),
+        assert_kind: Box::new(AssertKind::NullPointerDereference),
+    }
+}
diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs
index 95d3ce05000e..72460542f874 100644
--- a/compiler/rustc_mir_transform/src/check_pointers.rs
+++ b/compiler/rustc_mir_transform/src/check_pointers.rs
@@ -17,6 +17,7 @@ pub(crate) struct PointerCheck<'tcx> {
 /// [NonMutatingUseContext::SharedBorrow].
 #[derive(Copy, Clone)]
 pub(crate) enum BorrowCheckMode {
+    IncludeBorrows,
     ExcludeBorrows,
 }
 
@@ -168,7 +169,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
             ) => true,
             PlaceContext::MutatingUse(MutatingUseContext::Borrow)
             | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
-                !matches!(self.borrow_check_mode, BorrowCheckMode::ExcludeBorrows)
+                matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows)
             }
             _ => false,
         }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 0df9ce0c3d3f..c6a9e3ee0e6f 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -119,6 +119,7 @@ declare_passes! {
     mod check_call_recursion : CheckCallRecursion, CheckDropRecursion;
     mod check_alignment : CheckAlignment;
     mod check_const_item_mutation : CheckConstItemMutation;
+    mod check_null : CheckNull;
     mod check_packed_ref : CheckPackedRef;
     mod check_undefined_transmutes : CheckUndefinedTransmutes;
     // This pass is public to allow external drivers to perform MIR cleanup
@@ -643,6 +644,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &[
             // Add some UB checks before any UB gets optimized away.
             &check_alignment::CheckAlignment,
+            &check_null::CheckNull,
             // Before inlining: trim down MIR with passes to reduce inlining work.
 
             // Has to be done before inlining, otherwise actual call will be almost always inlined.
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index d53848f7461f..d22feb813695 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -814,6 +814,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
                 mir::AssertKind::MisalignedPointerDereference { .. } => {
                     push_mono_lang_item(self, LangItem::PanicMisalignedPointerDereference);
                 }
+                mir::AssertKind::NullPointerDereference => {
+                    push_mono_lang_item(self, LangItem::PanicNullPointerDereference);
+                }
                 _ => {
                     push_mono_lang_item(self, msg.panic_function());
                 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index aee98d7d410e..150ec02b7dba 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -496,6 +496,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
                     found: found.stable(tables),
                 }
             }
+            AssertKind::NullPointerDereference => {
+                stable_mir::mir::AssertMessage::NullPointerDereference
+            }
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6f1d3a74a816..37ae59bf2076 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1476,6 +1476,7 @@ symbols! {
         panic_location,
         panic_misaligned_pointer_dereference,
         panic_nounwind,
+        panic_null_pointer_dereference,
         panic_runtime,
         panic_str_2015,
         panic_unwind,
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 8686169c15d6..eec6cd8d49ba 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -251,6 +251,7 @@ pub enum AssertMessage {
     ResumedAfterReturn(CoroutineKind),
     ResumedAfterPanic(CoroutineKind),
     MisalignedPointerDereference { required: Operand, found: Operand },
+    NullPointerDereference,
 }
 
 impl AssertMessage {
@@ -306,6 +307,7 @@ impl AssertMessage {
             AssertMessage::MisalignedPointerDereference { .. } => {
                 Ok("misaligned pointer dereference")
             }
+            AssertMessage::NullPointerDereference => Ok("null pointer dereference occured"),
         }
     }
 }
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs
index 81981bce2026..6638f93e555b 100644
--- a/compiler/stable_mir/src/mir/pretty.rs
+++ b/compiler/stable_mir/src/mir/pretty.rs
@@ -298,6 +298,9 @@ fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::R
                 "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}"
             )
         }
+        AssertMessage::NullPointerDereference => {
+            write!(writer, "\"null pointer dereference occured.\"")
+        }
         AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => {
             write!(writer, "{}", msg.description().unwrap())
         }
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
index 79efb83cebde..d985a98fcbf0 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -426,7 +426,10 @@ pub trait MirVisitor {
             | AssertMessage::RemainderByZero(op) => {
                 self.visit_operand(op, location);
             }
-            AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { //nothing to visit
+            AssertMessage::ResumedAfterReturn(_)
+            | AssertMessage::ResumedAfterPanic(_)
+            | AssertMessage::NullPointerDereference => {
+                //nothing to visit
             }
             AssertMessage::MisalignedPointerDereference { required, found } => {
                 self.visit_operand(required, location);
diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs
index 53e2b238bae6..a89de12c02bd 100644
--- a/library/core/src/panicking.rs
+++ b/library/core/src/panicking.rs
@@ -291,6 +291,22 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
     )
 }
 
+#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))]
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
+#[track_caller]
+#[cfg_attr(not(bootstrap), lang = "panic_null_pointer_dereference")] // needed by codegen for panic on null pointer deref
+#[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind
+fn panic_null_pointer_dereference() -> ! {
+    if cfg!(feature = "panic_immediate_abort") {
+        super::intrinsics::abort()
+    }
+
+    panic_nounwind_fmt(
+        format_args!("null pointer dereference occured"),
+        /* force_no_backtrace */ false,
+    )
+}
+
 /// Panics because we cannot unwind out of a function.
 ///
 /// This is a separate function to avoid the codesize impact of each crate containing the string to
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 8201f332d333..c548f262a92f 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -166,6 +166,8 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
         // MIR passes can be enabled / disabled separately, we should figure out, what passes to
         // use for Clippy.
         config.opts.unstable_opts.mir_opt_level = Some(0);
+        config.opts.unstable_opts.mir_enable_passes =
+            vec![("CheckNull".to_owned(), false), ("CheckAlignment".to_owned(), false)];
 
         // Disable flattening and inlining of format_args!(), so the HIR matches with the AST.
         config.opts.unstable_opts.flatten_format_args = false;
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 2955dc38a8c2..3ec35763a7d0 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -168,7 +168,7 @@ pub const MIRI_DEFAULT_ARGS: &[&str] = &[
     "-Zmir-emit-retag",
     "-Zmir-keep-place-mention",
     "-Zmir-opt-level=0",
-    "-Zmir-enable-passes=-CheckAlignment",
+    "-Zmir-enable-passes=-CheckAlignment,-CheckNull",
     // Deduplicating diagnostics means we miss events when tracking what happens during an
     // execution. Let's not do that.
     "-Zdeduplicate-diagnostics=no",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index e83ce6dc42ce..38733577d1c8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -411,6 +411,7 @@ language_item_table! {
     PanicLocation,           sym::panic_location,      panic_location,             Target::Struct,         GenericRequirement::None;
     PanicImpl,               sym::panic_impl,          panic_impl,                 Target::Fn,             GenericRequirement::None;
     PanicCannotUnwind,       sym::panic_cannot_unwind, panic_cannot_unwind,        Target::Fn,             GenericRequirement::Exact(0);
+    PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None;
     /// libstd panic entry point. Necessary for const eval to be able to catch it
     BeginPanic,              sym::begin_panic,         begin_panic_fn,             Target::Fn,             GenericRequirement::None;
 
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index 9bc78ff87b8a..ae1c6efe0cb1 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -363,6 +363,7 @@ define_symbols! {
     panic_location,
     panic_misaligned_pointer_dereference,
     panic_nounwind,
+    panic_null_pointer_dereference,
     panic,
     Param,
     parse,
diff --git a/tests/mir-opt/null_check_references.rs b/tests/mir-opt/null_check_references.rs
new file mode 100644
index 000000000000..85f988656464
--- /dev/null
+++ b/tests/mir-opt/null_check_references.rs
@@ -0,0 +1,16 @@
+//@ compile-flags: -C debug-assertions
+
+struct Null {
+    a: u32,
+}
+
+fn main() {
+    // CHECK-LABEL: fn main(
+    // CHECK-NOT: {{assert.*}}
+    let val: u32 = 42;
+    let val_ref: &u32 = &val;
+    let _access1: &u32 = &*val_ref;
+
+    let val = Null { a: 42 };
+    let _access2: &u32 = &val.a;
+}
diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs
index b5af13ebfb5c..5e8b4e0dbf2a 100644
--- a/tests/ui/abi/segfault-no-out-of-stack.rs
+++ b/tests/ui/abi/segfault-no-out-of-stack.rs
@@ -1,5 +1,6 @@
 //@ run-pass
 //@ needs-subprocess
+//@ compile-flags: -Zub-checks=no -Zmir-enable-passes=-CheckNull
 //@ ignore-fuchsia must translate zircon signal to SIGSEGV/SIGBUS, FIXME (#58590)
 
 #![feature(rustc_private)]
diff --git a/tests/ui/mir/null/addrof_null.rs b/tests/ui/mir/null/addrof_null.rs
new file mode 100644
index 000000000000..0d0b7edeef62
--- /dev/null
+++ b/tests/ui/mir/null/addrof_null.rs
@@ -0,0 +1,14 @@
+// Make sure that we don't insert a check for `addr_of!`.
+//@ run-pass
+//@ compile-flags: -C debug-assertions
+
+struct Field {
+    a: u32,
+}
+
+fn main() {
+    unsafe {
+        let ptr: *const Field = std::ptr::null();
+        let _ptr = core::ptr::addr_of!((*ptr).a);
+    }
+}
diff --git a/tests/ui/mir/null/borrowed_mut_null.rs b/tests/ui/mir/null/borrowed_mut_null.rs
new file mode 100644
index 000000000000..437955c452b8
--- /dev/null
+++ b/tests/ui/mir/null/borrowed_mut_null.rs
@@ -0,0 +1,8 @@
+//@ run-fail
+//@ compile-flags: -C debug-assertions
+//@ error-pattern: null pointer dereference occured
+
+fn main() {
+    let ptr: *mut u32 = std::ptr::null_mut();
+    let _ptr: &mut u32 = unsafe { &mut *ptr };
+}
diff --git a/tests/ui/mir/null/borrowed_null.rs b/tests/ui/mir/null/borrowed_null.rs
new file mode 100644
index 000000000000..eb0794efaa53
--- /dev/null
+++ b/tests/ui/mir/null/borrowed_null.rs
@@ -0,0 +1,8 @@
+//@ run-fail
+//@ compile-flags: -C debug-assertions
+//@ error-pattern: null pointer dereference occured
+
+fn main() {
+    let ptr: *const u32 = std::ptr::null();
+    let _ptr: &u32 = unsafe { &*ptr };
+}
diff --git a/tests/ui/mir/null/null_lhs.rs b/tests/ui/mir/null/null_lhs.rs
new file mode 100644
index 000000000000..fd3bc3a78b82
--- /dev/null
+++ b/tests/ui/mir/null/null_lhs.rs
@@ -0,0 +1,10 @@
+//@ run-fail
+//@ compile-flags: -C debug-assertions
+//@ error-pattern: null pointer dereference occured
+
+fn main() {
+    let ptr: *mut u32 = std::ptr::null_mut();
+    unsafe {
+        *(ptr) = 42;
+    }
+}
diff --git a/tests/ui/mir/null/null_rhs.rs b/tests/ui/mir/null/null_rhs.rs
new file mode 100644
index 000000000000..45c8beb3fe86
--- /dev/null
+++ b/tests/ui/mir/null/null_rhs.rs
@@ -0,0 +1,10 @@
+//@ run-fail
+//@ compile-flags: -C debug-assertions
+//@ error-pattern: null pointer dereference occured
+
+fn main() {
+    let ptr: *mut u32 = std::ptr::null_mut();
+    unsafe {
+        let _v = *ptr;
+    }
+}
diff --git a/tests/ui/mir/null/place_without_read.rs b/tests/ui/mir/null/place_without_read.rs
new file mode 100644
index 000000000000..d6bfb089b011
--- /dev/null
+++ b/tests/ui/mir/null/place_without_read.rs
@@ -0,0 +1,10 @@
+// Make sure that we don't insert a check for places that do not read.
+//@ run-pass
+//@ compile-flags: -C debug-assertions
+
+fn main() {
+    let ptr: *const u16 = std::ptr::null();
+    unsafe {
+        let _ = *ptr;
+    }
+}
diff --git a/tests/ui/mir/null/two_pointers.rs b/tests/ui/mir/null/two_pointers.rs
new file mode 100644
index 000000000000..d9f0687fe0db
--- /dev/null
+++ b/tests/ui/mir/null/two_pointers.rs
@@ -0,0 +1,12 @@
+//@ run-fail
+//@ compile-flags: -C debug-assertions
+//@ error-pattern: null pointer dereference occured
+
+fn main() {
+    let ptr = std::ptr::null();
+    let mut dest = 0u32;
+    let dest_ptr = &mut dest as *mut u32;
+    unsafe {
+        *dest_ptr = *(ptr);
+    }
+}
diff --git a/tests/ui/mir/null/zero_sized_access.rs b/tests/ui/mir/null/zero_sized_access.rs
new file mode 100644
index 000000000000..e8aaf820c49e
--- /dev/null
+++ b/tests/ui/mir/null/zero_sized_access.rs
@@ -0,0 +1,15 @@
+// Make sure that we don't insert a check for zero-sized reads or writes to
+// null, because they are allowed.
+//@ run-pass
+//@ compile-flags: -C debug-assertions
+
+fn main() {
+    let ptr: *mut () = std::ptr::null_mut();
+    unsafe {
+        *(ptr) = ();
+    }
+    let ptr1: *const () = std::ptr::null_mut();
+    unsafe {
+        let _ptr = *ptr1;
+    }
+}

From 209bb8148318efda3c62dd0af197b00ce6ac3597 Mon Sep 17 00:00:00 2001
From: Alice Ryhl 
Date: Fri, 31 Jan 2025 11:37:41 +0000
Subject: [PATCH 337/342] Add documentation for derive(CoercePointee)

---
 library/core/src/marker.rs | 190 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 189 insertions(+), 1 deletion(-)

diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index a793fc2aa2e5..18ada14d101b 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1091,7 +1091,195 @@ pub trait FnPtr: Copy + Clone {
     fn addr(self) -> *const ();
 }
 
-/// Derive macro generating impls of traits related to smart pointers.
+/// Derive macro that makes a smart pointer usable with trait objects.
+///
+/// # What this macro does
+///
+/// This macro is intended to be used with user-defined pointer types, and makes it possible to
+/// perform coercions on the pointee of the user-defined pointer. There are two aspects to this:
+///
+/// ## Unsizing coercions of the pointee
+///
+/// By using the macro, the following example will compile:
+/// ```
+/// #![feature(derive_coerce_pointee)]
+/// use std::marker::CoercePointee;
+/// use std::ops::Deref;
+///
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// struct MySmartPointer(Box);
+///
+/// impl Deref for MySmartPointer {
+///     type Target = T;
+///     fn deref(&self) -> &T {
+///         &self.0
+///     }
+/// }
+///
+/// trait MyTrait {}
+///
+/// impl MyTrait for i32 {}
+///
+/// fn main() {
+///     let ptr: MySmartPointer = MySmartPointer(Box::new(4));
+///
+///     // This coercion would be an error without the derive.
+///     let ptr: MySmartPointer = ptr;
+/// }
+/// ```
+/// Without the `#[derive(CoercePointee)]` macro, this example would fail with the following error:
+/// ```text
+/// error[E0308]: mismatched types
+///   --> src/main.rs:11:44
+///    |
+/// 11 |     let ptr: MySmartPointer = ptr;
+///    |              ---------------------------   ^^^ expected `MySmartPointer`, found `MySmartPointer`
+///    |              |
+///    |              expected due to this
+///    |
+///    = note: expected struct `MySmartPointer`
+///               found struct `MySmartPointer`
+///    = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well
+/// ```
+///
+/// ## Dyn compatibility
+///
+/// This macro allows you to dispatch on the user-defined pointer type. That is, traits using the
+/// type as a receiver are dyn-compatible. For example, this compiles:
+///
+/// ```
+/// #![feature(arbitrary_self_types, derive_coerce_pointee)]
+/// use std::marker::CoercePointee;
+/// use std::ops::Deref;
+///
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// struct MySmartPointer(Box);
+///
+/// impl Deref for MySmartPointer {
+///     type Target = T;
+///     fn deref(&self) -> &T {
+///         &self.0
+///     }
+/// }
+///
+/// // You can always define this trait. (as long as you have #![feature(arbitrary_self_types)])
+/// trait MyTrait {
+///     fn func(self: MySmartPointer);
+/// }
+///
+/// // But using `dyn MyTrait` requires #[derive(CoercePointee)].
+/// fn call_func(value: MySmartPointer) {
+///     value.func();
+/// }
+/// ```
+/// If you remove the `#[derive(CoercePointee)]` annotation from the struct, then the above example
+/// will fail with this error message:
+/// ```text
+/// error[E0038]: the trait `MyTrait` is not dyn compatible
+///   --> src/lib.rs:21:36
+///    |
+/// 17 |     fn func(self: MySmartPointer);
+///    |                   -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self`
+/// ...
+/// 21 | fn call_func(value: MySmartPointer) {
+///    |                                    ^^^^^^^^^^^ `MyTrait` is not dyn compatible
+///    |
+/// note: for a trait to be dyn compatible it needs to allow building a vtable
+///       for more information, visit 
+///   --> src/lib.rs:17:19
+///    |
+/// 16 | trait MyTrait {
+///    |       ------- this trait is not dyn compatible...
+/// 17 |     fn func(self: MySmartPointer);
+///    |                   ^^^^^^^^^^^^^^^^^^^^ ...because method `func`'s `self` parameter cannot be dispatched on
+/// ```
+///
+/// # Requirements for using the macro
+///
+/// This macro can only be used if:
+/// * The type is a `#[repr(transparent)]` struct.
+/// * The type of its non-zero-sized field must either be a standard library pointer type
+///   (reference, raw pointer, `NonNull`, `Box`, `Rc`, `Arc`, etc.) or another user-defined type
+///   also using the `#[derive(CoercePointee)]` macro.
+/// * Zero-sized fields must not mention any generic parameters unless the zero-sized field has
+///   type [`PhantomData`].
+///
+/// ## Multiple type parameters
+///
+/// If the type has multiple type parameters, then you must explicitly specify which one should be
+/// used for dynamic dispatch. For example:
+/// ```
+/// # #![feature(derive_coerce_pointee)]
+/// # use std::marker::{CoercePointee, PhantomData};
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// struct MySmartPointer<#[pointee] T: ?Sized, U> {
+///     ptr: Box,
+///     _phantom: PhantomData,
+/// }
+/// ```
+/// Specifying `#[pointee]` when the struct has only one type parameter is allowed, but not required.
+///
+/// # Examples
+///
+/// A custom implementation of the `Rc` type:
+/// ```
+/// #![feature(derive_coerce_pointee)]
+/// use std::marker::CoercePointee;
+/// use std::ops::Deref;
+/// use std::ptr::NonNull;
+///
+/// #[derive(CoercePointee)]
+/// #[repr(transparent)]
+/// pub struct Rc {
+///     inner: NonNull>,
+/// }
+///
+/// struct RcInner {
+///     refcount: usize,
+///     value: T,
+/// }
+///
+/// impl Deref for Rc {
+///     type Target = T;
+///     fn deref(&self) -> &T {
+///         let ptr = self.inner.as_ptr();
+///         unsafe { &(*ptr).value }
+///     }
+/// }
+///
+/// impl Rc {
+///     pub fn new(value: T) -> Self {
+///         let inner = Box::new(RcInner {
+///             refcount: 1,
+///             value,
+///         });
+///         Self {
+///             inner: NonNull::from(Box::leak(inner)),
+///         }
+///     }
+/// }
+///
+/// impl Clone for Rc {
+///     fn clone(&self) -> Self {
+///         // A real implementation would handle overflow here.
+///         unsafe { (*self.inner.as_ptr()).refcount += 1 };
+///         Self { inner: self.inner }
+///     }
+/// }
+///
+/// impl Drop for Rc {
+///     fn drop(&mut self) {
+///         let ptr = self.inner.as_ptr();
+///         unsafe { (*ptr).refcount -= 1 };
+///         if unsafe { (*ptr).refcount } == 0 {
+///             drop(unsafe { Box::from_raw(ptr) });
+///         }
+///     }
+/// }
+/// ```
 #[rustc_builtin_macro(CoercePointee, attributes(pointee))]
 #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
 #[unstable(feature = "derive_coerce_pointee", issue = "123430")]

From d8b176f6836fa7efa0cdeb3488bceebbd5b219fb Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Wed, 22 Jan 2025 19:50:33 +0000
Subject: [PATCH 338/342] Move fulfillment error derivation into new module

---
 .../src/solve/fulfill.rs                      | 496 +----------------
 .../src/solve/fulfill/derive_errors.rs        | 499 ++++++++++++++++++
 .../rustc_trait_selection/src/traits/mod.rs   |   7 +-
 3 files changed, 506 insertions(+), 496 deletions(-)
 create mode 100644 compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index c8ae977b5ad6..0db44eda8470 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -1,25 +1,21 @@
 use std::marker::PhantomData;
 use std::mem;
-use std::ops::ControlFlow;
 
 use rustc_data_structures::thinvec::ExtractIf;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
 use rustc_infer::traits::{
-    self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause,
-    ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine,
+    FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
 };
-use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_middle::{bug, span_bug};
 use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
-use tracing::{instrument, trace};
+use tracing::instrument;
 
+use self::derive_errors::*;
 use super::Certainty;
 use super::delegate::SolverDelegate;
-use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
+use crate::traits::{FulfillmentError, ScrubbedTraitError};
+
+mod derive_errors;
 
 /// A trait engine using the new trait solver.
 ///
@@ -244,483 +240,3 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'
         }
     }
 }
-
-fn fulfillment_error_for_no_solution<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
-    let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
-
-    let code = match obligation.predicate.kind().skip_binder() {
-        ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
-            FulfillmentErrorCode::Project(
-                // FIXME: This could be a `Sorts` if the term is a type
-                MismatchedProjectionTypes { err: TypeError::Mismatch },
-            )
-        }
-        ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
-            let ct_ty = match ct.kind() {
-                ty::ConstKind::Unevaluated(uv) => {
-                    infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
-                }
-                ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
-                ty::ConstKind::Value(cv) => cv.ty,
-                kind => span_bug!(
-                    obligation.cause.span,
-                    "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
-                ),
-            };
-            FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
-                ct,
-                ct_ty,
-                expected_ty,
-            })
-        }
-        ty::PredicateKind::NormalizesTo(..) => {
-            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
-        }
-        ty::PredicateKind::AliasRelate(_, _, _) => {
-            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
-        }
-        ty::PredicateKind::Subtype(pred) => {
-            let (a, b) = infcx.enter_forall_and_leak_universe(
-                obligation.predicate.kind().rebind((pred.a, pred.b)),
-            );
-            let expected_found = ExpectedFound::new(a, b);
-            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
-        }
-        ty::PredicateKind::Coerce(pred) => {
-            let (a, b) = infcx.enter_forall_and_leak_universe(
-                obligation.predicate.kind().rebind((pred.a, pred.b)),
-            );
-            let expected_found = ExpectedFound::new(b, a);
-            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
-        }
-        ty::PredicateKind::Clause(_)
-        | ty::PredicateKind::DynCompatible(_)
-        | ty::PredicateKind::Ambiguous => {
-            FulfillmentErrorCode::Select(SelectionError::Unimplemented)
-        }
-        ty::PredicateKind::ConstEquate(..) => {
-            bug!("unexpected goal: {obligation:?}")
-        }
-    };
-
-    FulfillmentError { obligation, code, root_obligation }
-}
-
-fn fulfillment_error_for_stalled<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
-    let (code, refine_obligation) = infcx.probe(|_| {
-        match <&SolverDelegate<'tcx>>::from(infcx)
-            .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
-            .0
-        {
-            Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
-                (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
-            }
-            Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
-                FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
-                // Don't look into overflows because we treat overflows weirdly anyways.
-                // We discard the inference constraints from overflowing goals, so
-                // recomputing the goal again during `find_best_leaf_obligation` may apply
-                // inference guidance that makes other goals go from ambig -> pass, for example.
-                //
-                // FIXME: We should probably just look into overflows here.
-                false,
-            ),
-            Ok((_, Certainty::Yes)) => {
-                bug!("did not expect successful goal when collecting ambiguity errors")
-            }
-            Err(_) => {
-                bug!("did not expect selection error when collecting ambiguity errors")
-            }
-        }
-    });
-
-    FulfillmentError {
-        obligation: if refine_obligation {
-            find_best_leaf_obligation(infcx, &root_obligation, true)
-        } else {
-            root_obligation.clone()
-        },
-        code,
-        root_obligation,
-    }
-}
-
-fn fulfillment_error_for_overflow<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
-    FulfillmentError {
-        obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
-        code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
-        root_obligation,
-    }
-}
-
-fn find_best_leaf_obligation<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    obligation: &PredicateObligation<'tcx>,
-    consider_ambiguities: bool,
-) -> PredicateObligation<'tcx> {
-    let obligation = infcx.resolve_vars_if_possible(obligation.clone());
-    // FIXME: we use a probe here as the `BestObligation` visitor does not
-    // check whether it uses candidates which get shadowed by where-bounds.
-    //
-    // We should probably fix the visitor to not do so instead, as this also
-    // means the leaf obligation may be incorrect.
-    infcx
-        .fudge_inference_if_ok(|| {
-            infcx
-                .visit_proof_tree(obligation.clone().into(), &mut BestObligation {
-                    obligation: obligation.clone(),
-                    consider_ambiguities,
-                })
-                .break_value()
-                .ok_or(())
-        })
-        .unwrap_or(obligation)
-}
-
-struct BestObligation<'tcx> {
-    obligation: PredicateObligation<'tcx>,
-    consider_ambiguities: bool,
-}
-
-impl<'tcx> BestObligation<'tcx> {
-    fn with_derived_obligation(
-        &mut self,
-        derived_obligation: PredicateObligation<'tcx>,
-        and_then: impl FnOnce(&mut Self) -> >::Result,
-    ) -> >::Result {
-        let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
-        let res = and_then(self);
-        self.obligation = old_obligation;
-        res
-    }
-
-    /// Filter out the candidates that aren't interesting to visit for the
-    /// purposes of reporting errors. For ambiguities, we only consider
-    /// candidates that may hold. For errors, we only consider candidates that
-    /// *don't* hold and which have impl-where clauses that also don't hold.
-    fn non_trivial_candidates<'a>(
-        &self,
-        goal: &'a inspect::InspectGoal<'a, 'tcx>,
-    ) -> Vec> {
-        let mut candidates = goal.candidates();
-        match self.consider_ambiguities {
-            true => {
-                // If we have an ambiguous obligation, we must consider *all* candidates
-                // that hold, or else we may guide inference causing other goals to go
-                // from ambig -> pass/fail.
-                candidates.retain(|candidate| candidate.result().is_ok());
-            }
-            false => {
-                // If we have >1 candidate, one may still be due to "boring" reasons, like
-                // an alias-relate that failed to hold when deeply evaluated. We really
-                // don't care about reasons like this.
-                if candidates.len() > 1 {
-                    candidates.retain(|candidate| {
-                        goal.infcx().probe(|_| {
-                            candidate.instantiate_nested_goals(self.span()).iter().any(
-                                |nested_goal| {
-                                    matches!(
-                                        nested_goal.source(),
-                                        GoalSource::ImplWhereBound
-                                            | GoalSource::AliasBoundConstCondition
-                                            | GoalSource::InstantiateHigherRanked
-                                            | GoalSource::AliasWellFormed
-                                    ) && match self.consider_ambiguities {
-                                        true => {
-                                            matches!(
-                                                nested_goal.result(),
-                                                Ok(Certainty::Maybe(MaybeCause::Ambiguity))
-                                            )
-                                        }
-                                        false => matches!(nested_goal.result(), Err(_)),
-                                    }
-                                },
-                            )
-                        })
-                    });
-                }
-
-                // Prefer a non-rigid candidate if there is one.
-                if candidates.len() > 1 {
-                    candidates.retain(|candidate| {
-                        !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
-                    });
-                }
-            }
-        }
-
-        candidates
-    }
-}
-
-impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
-    type Result = ControlFlow>;
-
-    fn span(&self) -> rustc_span::Span {
-        self.obligation.cause.span
-    }
-
-    #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
-    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
-        let candidates = self.non_trivial_candidates(goal);
-        trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::>());
-
-        let [candidate] = candidates.as_slice() else {
-            return ControlFlow::Break(self.obligation.clone());
-        };
-
-        // Don't walk into impls that have `do_not_recommend`.
-        if let inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::Impl(impl_def_id),
-            result: _,
-        } = candidate.kind()
-            && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
-        {
-            return ControlFlow::Break(self.obligation.clone());
-        }
-
-        let tcx = goal.infcx().tcx;
-        // FIXME: Also, what about considering >1 layer up the stack? May be necessary
-        // for normalizes-to.
-        let pred_kind = goal.goal().predicate.kind();
-        let child_mode = match pred_kind.skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
-                ChildMode::Trait(pred_kind.rebind(pred))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
-                ChildMode::Host(pred_kind.rebind(pred))
-            }
-            ty::PredicateKind::NormalizesTo(normalizes_to)
-                if matches!(
-                    normalizes_to.alias.kind(tcx),
-                    ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
-                ) =>
-            {
-                ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
-                    trait_ref: normalizes_to.alias.trait_ref(tcx),
-                    polarity: ty::PredicatePolarity::Positive,
-                }))
-            }
-            _ => ChildMode::PassThrough,
-        };
-
-        let nested_goals = candidate.instantiate_nested_goals(self.span());
-
-        // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
-        // an actual candidate, instead we should treat them as if the impl was never considered to
-        // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written
-        // instead of `impl Trait for T`.
-        //
-        // We do this as a separate loop so that we do not choose to tell the user about some nested
-        // goal before we encounter a `T: FnPtr` nested goal.
-        for nested_goal in &nested_goals {
-            if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
-                && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
-                && poly_trait_pred.def_id() == fn_ptr_trait
-                && let Err(NoSolution) = nested_goal.result()
-            {
-                return ControlFlow::Break(self.obligation.clone());
-            }
-        }
-
-        let mut impl_where_bound_count = 0;
-        for nested_goal in nested_goals {
-            trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
-
-            let make_obligation = |cause| Obligation {
-                cause,
-                param_env: nested_goal.goal().param_env,
-                predicate: nested_goal.goal().predicate,
-                recursion_depth: self.obligation.recursion_depth + 1,
-            };
-
-            let obligation;
-            match (child_mode, nested_goal.source()) {
-                (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
-                    continue;
-                }
-                (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
-                    obligation = make_obligation(derive_cause(
-                        tcx,
-                        candidate.kind(),
-                        self.obligation.cause.clone(),
-                        impl_where_bound_count,
-                        parent_trait_pred,
-                    ));
-                    impl_where_bound_count += 1;
-                }
-                (
-                    ChildMode::Host(parent_host_pred),
-                    GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
-                ) => {
-                    obligation = make_obligation(derive_host_cause(
-                        tcx,
-                        candidate.kind(),
-                        self.obligation.cause.clone(),
-                        impl_where_bound_count,
-                        parent_host_pred,
-                    ));
-                    impl_where_bound_count += 1;
-                }
-                // Skip over a higher-ranked predicate.
-                (_, GoalSource::InstantiateHigherRanked) => {
-                    obligation = self.obligation.clone();
-                }
-                (ChildMode::PassThrough, _)
-                | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
-                    obligation = make_obligation(self.obligation.cause.clone());
-                }
-            }
-
-            // Skip nested goals that aren't the *reason* for our goal's failure.
-            match self.consider_ambiguities {
-                true if matches!(
-                    nested_goal.result(),
-                    Ok(Certainty::Maybe(MaybeCause::Ambiguity))
-                ) => {}
-                false if matches!(nested_goal.result(), Err(_)) => {}
-                _ => continue,
-            }
-
-            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
-        }
-
-        // alias-relate may fail because the lhs or rhs can't be normalized,
-        // and therefore is treated as rigid.
-        if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
-            if let Some(obligation) = goal
-                .infcx()
-                .visit_proof_tree_at_depth(
-                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
-                    goal.depth() + 1,
-                    self,
-                )
-                .break_value()
-            {
-                return ControlFlow::Break(obligation);
-            } else if let Some(obligation) = goal
-                .infcx()
-                .visit_proof_tree_at_depth(
-                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
-                    goal.depth() + 1,
-                    self,
-                )
-                .break_value()
-            {
-                return ControlFlow::Break(obligation);
-            }
-        }
-
-        ControlFlow::Break(self.obligation.clone())
-    }
-}
-
-#[derive(Debug, Copy, Clone)]
-enum ChildMode<'tcx> {
-    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
-    // and skip all `GoalSource::Misc`, which represent useless obligations
-    // such as alias-eq which may not hold.
-    Trait(ty::PolyTraitPredicate<'tcx>),
-    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
-    // and skip all `GoalSource::Misc`, which represent useless obligations
-    // such as alias-eq which may not hold.
-    Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
-    // Skip trying to derive an `ObligationCause` from this obligation, and
-    // report *all* sub-obligations as if they came directly from the parent
-    // obligation.
-    PassThrough,
-}
-
-fn derive_cause<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    candidate_kind: inspect::ProbeKind>,
-    mut cause: ObligationCause<'tcx>,
-    idx: usize,
-    parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-) -> ObligationCause<'tcx> {
-    match candidate_kind {
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::Impl(impl_def_id),
-            result: _,
-        } => {
-            if let Some((_, span)) =
-                tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
-            {
-                cause = cause.derived_cause(parent_trait_pred, |derived| {
-                    ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
-                        derived,
-                        impl_or_alias_def_id: impl_def_id,
-                        impl_def_predicate_index: Some(idx),
-                        span,
-                    }))
-                })
-            }
-        }
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::BuiltinImpl(..),
-            result: _,
-        } => {
-            cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
-        }
-        _ => {}
-    };
-    cause
-}
-
-fn derive_host_cause<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    candidate_kind: inspect::ProbeKind>,
-    mut cause: ObligationCause<'tcx>,
-    idx: usize,
-    parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
-) -> ObligationCause<'tcx> {
-    match candidate_kind {
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::Impl(impl_def_id),
-            result: _,
-        } => {
-            if let Some((_, span)) = tcx
-                .predicates_of(impl_def_id)
-                .instantiate_identity(tcx)
-                .into_iter()
-                .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
-                    |(trait_ref, span)| {
-                        (
-                            trait_ref.to_host_effect_clause(
-                                tcx,
-                                parent_host_pred.skip_binder().constness,
-                            ),
-                            span,
-                        )
-                    },
-                ))
-                .nth(idx)
-            {
-                cause =
-                    cause.derived_host_cause(parent_host_pred, |derived| {
-                        ObligationCauseCode::ImplDerivedHost(Box::new(
-                            traits::ImplDerivedHostCause { derived, impl_def_id, span },
-                        ))
-                    })
-            }
-        }
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::BuiltinImpl(..),
-            result: _,
-        } => {
-            cause =
-                cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
-        }
-        _ => {}
-    };
-    cause
-}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
new file mode 100644
index 000000000000..2c55f13a0dff
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -0,0 +1,499 @@
+use std::ops::ControlFlow;
+
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
+use rustc_infer::traits::{
+    self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
+    PredicateObligation, SelectionError,
+};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::{bug, span_bug};
+use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
+use rustc_type_ir::solve::NoSolution;
+use tracing::{instrument, trace};
+
+use crate::solve::Certainty;
+use crate::solve::delegate::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
+use crate::traits::{FulfillmentError, FulfillmentErrorCode};
+
+pub(super) fn fulfillment_error_for_no_solution<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+    let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
+
+    let code = match obligation.predicate.kind().skip_binder() {
+        ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
+            FulfillmentErrorCode::Project(
+                // FIXME: This could be a `Sorts` if the term is a type
+                MismatchedProjectionTypes { err: TypeError::Mismatch },
+            )
+        }
+        ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
+            let ct_ty = match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => {
+                    infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
+                }
+                ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
+                ty::ConstKind::Value(cv) => cv.ty,
+                kind => span_bug!(
+                    obligation.cause.span,
+                    "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
+                ),
+            };
+            FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
+                ct,
+                ct_ty,
+                expected_ty,
+            })
+        }
+        ty::PredicateKind::NormalizesTo(..) => {
+            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+        }
+        ty::PredicateKind::AliasRelate(_, _, _) => {
+            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+        }
+        ty::PredicateKind::Subtype(pred) => {
+            let (a, b) = infcx.enter_forall_and_leak_universe(
+                obligation.predicate.kind().rebind((pred.a, pred.b)),
+            );
+            let expected_found = ExpectedFound::new(a, b);
+            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+        }
+        ty::PredicateKind::Coerce(pred) => {
+            let (a, b) = infcx.enter_forall_and_leak_universe(
+                obligation.predicate.kind().rebind((pred.a, pred.b)),
+            );
+            let expected_found = ExpectedFound::new(b, a);
+            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+        }
+        ty::PredicateKind::Clause(_)
+        | ty::PredicateKind::DynCompatible(_)
+        | ty::PredicateKind::Ambiguous => {
+            FulfillmentErrorCode::Select(SelectionError::Unimplemented)
+        }
+        ty::PredicateKind::ConstEquate(..) => {
+            bug!("unexpected goal: {obligation:?}")
+        }
+    };
+
+    FulfillmentError { obligation, code, root_obligation }
+}
+
+pub(super) fn fulfillment_error_for_stalled<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+    let (code, refine_obligation) = infcx.probe(|_| {
+        match <&SolverDelegate<'tcx>>::from(infcx)
+            .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
+            .0
+        {
+            Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
+                (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
+            }
+            Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
+                FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
+                // Don't look into overflows because we treat overflows weirdly anyways.
+                // We discard the inference constraints from overflowing goals, so
+                // recomputing the goal again during `find_best_leaf_obligation` may apply
+                // inference guidance that makes other goals go from ambig -> pass, for example.
+                //
+                // FIXME: We should probably just look into overflows here.
+                false,
+            ),
+            Ok((_, Certainty::Yes)) => {
+                bug!("did not expect successful goal when collecting ambiguity errors")
+            }
+            Err(_) => {
+                bug!("did not expect selection error when collecting ambiguity errors")
+            }
+        }
+    });
+
+    FulfillmentError {
+        obligation: if refine_obligation {
+            find_best_leaf_obligation(infcx, &root_obligation, true)
+        } else {
+            root_obligation.clone()
+        },
+        code,
+        root_obligation,
+    }
+}
+
+pub(super) fn fulfillment_error_for_overflow<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+    FulfillmentError {
+        obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
+        code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
+        root_obligation,
+    }
+}
+
+fn find_best_leaf_obligation<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligation: &PredicateObligation<'tcx>,
+    consider_ambiguities: bool,
+) -> PredicateObligation<'tcx> {
+    let obligation = infcx.resolve_vars_if_possible(obligation.clone());
+    // FIXME: we use a probe here as the `BestObligation` visitor does not
+    // check whether it uses candidates which get shadowed by where-bounds.
+    //
+    // We should probably fix the visitor to not do so instead, as this also
+    // means the leaf obligation may be incorrect.
+    infcx
+        .fudge_inference_if_ok(|| {
+            infcx
+                .visit_proof_tree(obligation.clone().into(), &mut BestObligation {
+                    obligation: obligation.clone(),
+                    consider_ambiguities,
+                })
+                .break_value()
+                .ok_or(())
+        })
+        .unwrap_or(obligation)
+}
+
+struct BestObligation<'tcx> {
+    obligation: PredicateObligation<'tcx>,
+    consider_ambiguities: bool,
+}
+
+impl<'tcx> BestObligation<'tcx> {
+    fn with_derived_obligation(
+        &mut self,
+        derived_obligation: PredicateObligation<'tcx>,
+        and_then: impl FnOnce(&mut Self) -> >::Result,
+    ) -> >::Result {
+        let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
+        let res = and_then(self);
+        self.obligation = old_obligation;
+        res
+    }
+
+    /// Filter out the candidates that aren't interesting to visit for the
+    /// purposes of reporting errors. For ambiguities, we only consider
+    /// candidates that may hold. For errors, we only consider candidates that
+    /// *don't* hold and which have impl-where clauses that also don't hold.
+    fn non_trivial_candidates<'a>(
+        &self,
+        goal: &'a inspect::InspectGoal<'a, 'tcx>,
+    ) -> Vec> {
+        let mut candidates = goal.candidates();
+        match self.consider_ambiguities {
+            true => {
+                // If we have an ambiguous obligation, we must consider *all* candidates
+                // that hold, or else we may guide inference causing other goals to go
+                // from ambig -> pass/fail.
+                candidates.retain(|candidate| candidate.result().is_ok());
+            }
+            false => {
+                // If we have >1 candidate, one may still be due to "boring" reasons, like
+                // an alias-relate that failed to hold when deeply evaluated. We really
+                // don't care about reasons like this.
+                if candidates.len() > 1 {
+                    candidates.retain(|candidate| {
+                        goal.infcx().probe(|_| {
+                            candidate.instantiate_nested_goals(self.span()).iter().any(
+                                |nested_goal| {
+                                    matches!(
+                                        nested_goal.source(),
+                                        GoalSource::ImplWhereBound
+                                            | GoalSource::AliasBoundConstCondition
+                                            | GoalSource::InstantiateHigherRanked
+                                            | GoalSource::AliasWellFormed
+                                    ) && match self.consider_ambiguities {
+                                        true => {
+                                            matches!(
+                                                nested_goal.result(),
+                                                Ok(Certainty::Maybe(MaybeCause::Ambiguity))
+                                            )
+                                        }
+                                        false => matches!(nested_goal.result(), Err(_)),
+                                    }
+                                },
+                            )
+                        })
+                    });
+                }
+
+                // Prefer a non-rigid candidate if there is one.
+                if candidates.len() > 1 {
+                    candidates.retain(|candidate| {
+                        !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
+                    });
+                }
+            }
+        }
+
+        candidates
+    }
+}
+
+impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
+    type Result = ControlFlow>;
+
+    fn span(&self) -> rustc_span::Span {
+        self.obligation.cause.span
+    }
+
+    #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
+    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
+        let candidates = self.non_trivial_candidates(goal);
+        trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::>());
+
+        let [candidate] = candidates.as_slice() else {
+            return ControlFlow::Break(self.obligation.clone());
+        };
+
+        // Don't walk into impls that have `do_not_recommend`.
+        if let inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } = candidate.kind()
+            && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
+        {
+            return ControlFlow::Break(self.obligation.clone());
+        }
+
+        let tcx = goal.infcx().tcx;
+        // FIXME: Also, what about considering >1 layer up the stack? May be necessary
+        // for normalizes-to.
+        let pred_kind = goal.goal().predicate.kind();
+        let child_mode = match pred_kind.skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+                ChildMode::Trait(pred_kind.rebind(pred))
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
+                ChildMode::Host(pred_kind.rebind(pred))
+            }
+            ty::PredicateKind::NormalizesTo(normalizes_to)
+                if matches!(
+                    normalizes_to.alias.kind(tcx),
+                    ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
+                ) =>
+            {
+                ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
+                    trait_ref: normalizes_to.alias.trait_ref(tcx),
+                    polarity: ty::PredicatePolarity::Positive,
+                }))
+            }
+            _ => ChildMode::PassThrough,
+        };
+
+        let nested_goals = candidate.instantiate_nested_goals(self.span());
+
+        // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
+        // an actual candidate, instead we should treat them as if the impl was never considered to
+        // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written
+        // instead of `impl Trait for T`.
+        //
+        // We do this as a separate loop so that we do not choose to tell the user about some nested
+        // goal before we encounter a `T: FnPtr` nested goal.
+        for nested_goal in &nested_goals {
+            if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
+                && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+                && poly_trait_pred.def_id() == fn_ptr_trait
+                && let Err(NoSolution) = nested_goal.result()
+            {
+                return ControlFlow::Break(self.obligation.clone());
+            }
+        }
+
+        let mut impl_where_bound_count = 0;
+        for nested_goal in nested_goals {
+            trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
+
+            let make_obligation = |cause| Obligation {
+                cause,
+                param_env: nested_goal.goal().param_env,
+                predicate: nested_goal.goal().predicate,
+                recursion_depth: self.obligation.recursion_depth + 1,
+            };
+
+            let obligation;
+            match (child_mode, nested_goal.source()) {
+                (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
+                    continue;
+                }
+                (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
+                    obligation = make_obligation(derive_cause(
+                        tcx,
+                        candidate.kind(),
+                        self.obligation.cause.clone(),
+                        impl_where_bound_count,
+                        parent_trait_pred,
+                    ));
+                    impl_where_bound_count += 1;
+                }
+                (
+                    ChildMode::Host(parent_host_pred),
+                    GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
+                ) => {
+                    obligation = make_obligation(derive_host_cause(
+                        tcx,
+                        candidate.kind(),
+                        self.obligation.cause.clone(),
+                        impl_where_bound_count,
+                        parent_host_pred,
+                    ));
+                    impl_where_bound_count += 1;
+                }
+                // Skip over a higher-ranked predicate.
+                (_, GoalSource::InstantiateHigherRanked) => {
+                    obligation = self.obligation.clone();
+                }
+                (ChildMode::PassThrough, _)
+                | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
+                    obligation = make_obligation(self.obligation.cause.clone());
+                }
+            }
+
+            // Skip nested goals that aren't the *reason* for our goal's failure.
+            match self.consider_ambiguities {
+                true if matches!(
+                    nested_goal.result(),
+                    Ok(Certainty::Maybe(MaybeCause::Ambiguity))
+                ) => {}
+                false if matches!(nested_goal.result(), Err(_)) => {}
+                _ => continue,
+            }
+
+            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+        }
+
+        // alias-relate may fail because the lhs or rhs can't be normalized,
+        // and therefore is treated as rigid.
+        if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
+            if let Some(obligation) = goal
+                .infcx()
+                .visit_proof_tree_at_depth(
+                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
+                    goal.depth() + 1,
+                    self,
+                )
+                .break_value()
+            {
+                return ControlFlow::Break(obligation);
+            } else if let Some(obligation) = goal
+                .infcx()
+                .visit_proof_tree_at_depth(
+                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
+                    goal.depth() + 1,
+                    self,
+                )
+                .break_value()
+            {
+                return ControlFlow::Break(obligation);
+            }
+        }
+
+        ControlFlow::Break(self.obligation.clone())
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum ChildMode<'tcx> {
+    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+    // and skip all `GoalSource::Misc`, which represent useless obligations
+    // such as alias-eq which may not hold.
+    Trait(ty::PolyTraitPredicate<'tcx>),
+    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+    // and skip all `GoalSource::Misc`, which represent useless obligations
+    // such as alias-eq which may not hold.
+    Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
+    // Skip trying to derive an `ObligationCause` from this obligation, and
+    // report *all* sub-obligations as if they came directly from the parent
+    // obligation.
+    PassThrough,
+}
+
+fn derive_cause<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    candidate_kind: inspect::ProbeKind>,
+    mut cause: ObligationCause<'tcx>,
+    idx: usize,
+    parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+) -> ObligationCause<'tcx> {
+    match candidate_kind {
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } => {
+            if let Some((_, span)) =
+                tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
+            {
+                cause = cause.derived_cause(parent_trait_pred, |derived| {
+                    ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
+                        derived,
+                        impl_or_alias_def_id: impl_def_id,
+                        impl_def_predicate_index: Some(idx),
+                        span,
+                    }))
+                })
+            }
+        }
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::BuiltinImpl(..),
+            result: _,
+        } => {
+            cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
+        }
+        _ => {}
+    };
+    cause
+}
+
+fn derive_host_cause<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    candidate_kind: inspect::ProbeKind>,
+    mut cause: ObligationCause<'tcx>,
+    idx: usize,
+    parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+) -> ObligationCause<'tcx> {
+    match candidate_kind {
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } => {
+            if let Some((_, span)) = tcx
+                .predicates_of(impl_def_id)
+                .instantiate_identity(tcx)
+                .into_iter()
+                .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
+                    |(trait_ref, span)| {
+                        (
+                            trait_ref.to_host_effect_clause(
+                                tcx,
+                                parent_host_pred.skip_binder().constness,
+                            ),
+                            span,
+                        )
+                    },
+                ))
+                .nth(idx)
+            {
+                cause =
+                    cause.derived_host_cause(parent_host_pred, |derived| {
+                        ObligationCauseCode::ImplDerivedHost(Box::new(
+                            traits::ImplDerivedHostCause { derived, impl_def_id, span },
+                        ))
+                    })
+            }
+        }
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::BuiltinImpl(..),
+            result: _,
+        } => {
+            cause =
+                cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
+        }
+        _ => {}
+    };
+    cause
+}
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 6b5ebade6aed..d4a9664e2828 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
 use crate::regions::InferCtxtRegionExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 
+#[derive(Debug)]
 pub struct FulfillmentError<'tcx> {
     pub obligation: PredicateObligation<'tcx>,
     pub code: FulfillmentErrorCode<'tcx>,
@@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> {
     }
 }
 
-impl<'tcx> Debug for FulfillmentError<'tcx> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
-    }
-}
-
 #[derive(Clone)]
 pub enum FulfillmentErrorCode<'tcx> {
     /// Inherently impossible to fulfill; this trait is implemented if and only

From 304b3cfcb298c6bd6f7644ec3c78f28dd72eba1c Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Wed, 22 Jan 2025 20:13:36 +0000
Subject: [PATCH 339/342] Manually walk into WF obligations in BestObligation
 proof tree visitor

---
 .../src/solve/delegate.rs                     |  9 +-
 .../src/solve/fulfill/derive_errors.rs        | 60 ++++++++++----
 .../src/solve/inspect/analyse.rs              | 82 +++++++++++--------
 .../query/type_op/implied_outlives_bounds.rs  |  6 +-
 .../rustc_trait_selection/src/traits/wf.rs    | 10 ++-
 .../bugs/wf-check-skipped.next.stderr         |  2 +
 .../const-generics/issues/issue-88119.stderr  | 42 ++++------
 .../as_expression.next.stderr                 | 12 +--
 .../do_not_recommend/as_expression.rs         |  4 +-
 .../in-trait/alias-bounds-when-not-wf.stderr  |  5 ++
 ...st-region-infer-to-static-in-binder.stderr |  4 +-
 .../specialization-transmute.stderr           |  7 +-
 .../const-in-impl-fn-return-type.next.stderr  |  2 +
 tests/ui/union/union-derive-eq.next.stderr    |  2 +
 .../ui/wf/wf-normalization-sized.next.stderr  |  6 ++
 tests/ui/wf/wf-trait-fn-arg.next.stderr       |  5 ++
 tests/ui/wf/wf-trait-fn-ret.next.stderr       |  5 ++
 .../wf/wf-trait-fn-where-clause.next.stderr   |  5 ++
 18 files changed, 164 insertions(+), 104 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index acd00d9f74f5..abb794934324 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -1,7 +1,7 @@
 use std::ops::Deref;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
 use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
 use rustc_infer::infer::canonical::{
     Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
@@ -98,9 +98,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         param_env: ty::ParamEnv<'tcx>,
         arg: ty::GenericArg<'tcx>,
     ) -> Option>>> {
-        crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
-            obligations.into_iter().map(|obligation| obligation.into()).collect()
-        })
+        crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
+            .map(|obligations| {
+                obligations.into_iter().map(|obligation| obligation.into()).collect()
+            })
     }
 
     fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
index 2c55f13a0dff..c64bc19835ba 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -10,13 +10,13 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
-use rustc_type_ir::solve::NoSolution;
+use rustc_type_ir::solve::{Goal, NoSolution};
 use tracing::{instrument, trace};
 
 use crate::solve::Certainty;
 use crate::solve::delegate::SolverDelegate;
 use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use crate::traits::{FulfillmentError, FulfillmentErrorCode};
+use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
 
 pub(super) fn fulfillment_error_for_no_solution<'tcx>(
     infcx: &InferCtxt<'tcx>,
@@ -207,14 +207,10 @@ impl<'tcx> BestObligation<'tcx> {
                                             | GoalSource::AliasBoundConstCondition
                                             | GoalSource::InstantiateHigherRanked
                                             | GoalSource::AliasWellFormed
-                                    ) && match self.consider_ambiguities {
-                                        true => {
-                                            matches!(
-                                                nested_goal.result(),
-                                                Ok(Certainty::Maybe(MaybeCause::Ambiguity))
-                                            )
-                                        }
-                                        false => matches!(nested_goal.result(), Err(_)),
+                                    ) && match (self.consider_ambiguities, nested_goal.result()) {
+                                        (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity)))
+                                        | (false, Err(_)) => true,
+                                        _ => false,
                                     }
                                 },
                             )
@@ -233,6 +229,39 @@ impl<'tcx> BestObligation<'tcx> {
 
         candidates
     }
+
+    /// HACK: We walk the nested obligations for a well-formed arg manually,
+    /// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
+    /// Ideally we'd be able to track this better.
+    fn visit_well_formed_goal(
+        &mut self,
+        candidate: &inspect::InspectCandidate<'_, 'tcx>,
+        arg: ty::GenericArg<'tcx>,
+    ) -> ControlFlow> {
+        let infcx = candidate.goal().infcx();
+        let param_env = candidate.goal().goal().param_env;
+        let body_id = self.obligation.cause.body_id;
+
+        for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id)
+            .into_iter()
+            .flatten()
+        {
+            let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
+                GoalSource::Misc,
+                Goal::new(infcx.tcx, obligation.param_env, obligation.predicate),
+                self.span(),
+            );
+            // Skip nested goals that aren't the *reason* for our goal's failure.
+            match (self.consider_ambiguities, nested_goal.result()) {
+                (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
+                _ => continue,
+            }
+
+            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+        }
+
+        ControlFlow::Break(self.obligation.clone())
+    }
 }
 
 impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
@@ -283,6 +312,9 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                     polarity: ty::PredicatePolarity::Positive,
                 }))
             }
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                return self.visit_well_formed_goal(candidate, arg);
+            }
             _ => ChildMode::PassThrough,
         };
 
@@ -355,12 +387,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
             }
 
             // Skip nested goals that aren't the *reason* for our goal's failure.
-            match self.consider_ambiguities {
-                true if matches!(
-                    nested_goal.result(),
-                    Ok(Certainty::Maybe(MaybeCause::Ambiguity))
-                ) => {}
-                false if matches!(nested_goal.result(), Err(_)) => {}
+            match (self.consider_ambiguities, nested_goal.result()) {
+                (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
                 _ => continue,
             }
 
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index e735020a63e6..9ba48cd588fa 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
 
         let goals = instantiated_goals
             .into_iter()
-            .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
-                Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
-                    let unconstrained_term = match term.unpack() {
-                        ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
-                        ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
-                    };
-                    let goal =
-                        goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
-                    // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
-                    // expected term. This means that candidates which only fail due to nested goals
-                    // and which normalize to a different term then the final result could ICE: when
-                    // building their proof tree, the expected term was unconstrained, but when
-                    // instantiating the candidate it is already constrained to the result of another
-                    // candidate.
-                    let proof_tree = infcx
-                        .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
-                    InspectGoal::new(
-                        infcx,
-                        self.goal.depth + 1,
-                        proof_tree.unwrap(),
-                        Some(NormalizesToTermHack { term, unconstrained_term }),
-                        source,
-                    )
-                }
-                _ => {
-                    // We're using a probe here as evaluating a goal could constrain
-                    // inference variables by choosing one candidate. If we then recurse
-                    // into another candidate who ends up with different inference
-                    // constraints, we get an ICE if we already applied the constraints
-                    // from the chosen candidate.
-                    let proof_tree = infcx
-                        .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
-                        .unwrap();
-                    InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
-                }
-            })
+            .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
             .collect();
 
         (goals, opt_impl_args)
     }
 
+    pub fn instantiate_proof_tree_for_nested_goal(
+        &self,
+        source: GoalSource,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        span: Span,
+    ) -> InspectGoal<'a, 'tcx> {
+        let infcx = self.goal.infcx;
+        match goal.predicate.kind().no_bound_vars() {
+            Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
+                let unconstrained_term = match term.unpack() {
+                    ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
+                    ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
+                };
+                let goal =
+                    goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
+                // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
+                // expected term. This means that candidates which only fail due to nested goals
+                // and which normalize to a different term then the final result could ICE: when
+                // building their proof tree, the expected term was unconstrained, but when
+                // instantiating the candidate it is already constrained to the result of another
+                // candidate.
+                let proof_tree =
+                    infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
+                InspectGoal::new(
+                    infcx,
+                    self.goal.depth + 1,
+                    proof_tree.unwrap(),
+                    Some(NormalizesToTermHack { term, unconstrained_term }),
+                    source,
+                )
+            }
+            _ => {
+                // We're using a probe here as evaluating a goal could constrain
+                // inference variables by choosing one candidate. If we then recurse
+                // into another candidate who ends up with different inference
+                // constraints, we get an ICE if we already applied the constraints
+                // from the chosen candidate.
+                let proof_tree = infcx
+                    .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
+                    .unwrap();
+                InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
+            }
+        }
+    }
+
     /// Visit all nested goals of this candidate, rolling back
     /// all inference constraints.
     pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 1339739ce7f5..ec0b79033969 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
 use rustc_middle::infer::canonical::CanonicalQueryResponse;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
-use rustc_span::Span;
 use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::{DUMMY_SP, Span};
 use rustc_type_ir::outlives::{Component, push_outlives_components};
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
@@ -92,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
 
         // From the full set of obligations, just filter down to the region relationships.
         for obligation in
-            wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
+            wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
+                .into_iter()
+                .flatten()
         {
             assert!(!obligation.has_escaping_bound_vars());
             let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 20b675bcb76b..37d49cc2f555 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -8,8 +8,8 @@ use rustc_middle::ty::{
     self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
     TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
-use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::Span;
+use rustc_span::def_id::{DefId, LocalDefId};
 use tracing::{debug, instrument, trace};
 
 use crate::infer::InferCtxt;
@@ -89,6 +89,8 @@ pub fn unnormalized_obligations<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     arg: GenericArg<'tcx>,
+    span: Span,
+    body_id: LocalDefId,
 ) -> Option> {
     debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
 
@@ -106,8 +108,8 @@ pub fn unnormalized_obligations<'tcx>(
     let mut wf = WfPredicates {
         infcx,
         param_env,
-        body_id: CRATE_DEF_ID,
-        span: DUMMY_SP,
+        body_id,
+        span,
         out: PredicateObligations::new(),
         recursion_depth: 0,
         item: None,
diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr
index bf53089675d5..81ace4ebb6da 100644
--- a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr
+++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr
@@ -5,6 +5,8 @@ LL | fn main() -> Foo::Bar::> {}
    |                         ^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[u32]`
+note: required by an implicit `Sized` bound in `Vec`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr
index f219c90849a2..c497f1b6d0bd 100644
--- a/tests/ui/const-generics/issues/issue-88119.stderr
+++ b/tests/ui/const-generics/issues/issue-88119.stderr
@@ -6,35 +6,29 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
    |
    = help: remove one of these features
 
-error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}`
-  --> $DIR/issue-88119.rs:19:49
+error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated`
+  --> $DIR/issue-88119.rs:21:5
    |
-LL | impl const ConstName for &T
-   |                                                 ^^ cannot normalize `<&T as ConstName>::{constant#0}`
-   |
-note: required for `&T` to implement `~const ConstName`
-  --> $DIR/issue-88119.rs:19:35
-   |
-LL | impl const ConstName for &T
-   |                                   ^^^^^^^^^     ^^
-LL | where
 LL |     [(); name_len::()]:,
-   |     --------------------- unsatisfied trait bound introduced here
+   |     ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated`
+   |
+note: required by a bound in `<&T as ConstName>`
+  --> $DIR/issue-88119.rs:21:10
+   |
+LL |     [(); name_len::()]:,
+   |          ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>`
 
-error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
-  --> $DIR/issue-88119.rs:26:49
+error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated`
+  --> $DIR/issue-88119.rs:28:5
    |
-LL | impl const ConstName for &mut T
-   |                                                 ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
-   |
-note: required for `&mut T` to implement `~const ConstName`
-  --> $DIR/issue-88119.rs:26:35
-   |
-LL | impl const ConstName for &mut T
-   |                                   ^^^^^^^^^     ^^^^^^
-LL | where
 LL |     [(); name_len::()]:,
-   |     --------------------- unsatisfied trait bound introduced here
+   |     ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated`
+   |
+note: required by a bound in `<&mut T as ConstName>`
+  --> $DIR/issue-88119.rs:28:10
+   |
+LL |     [(); name_len::()]:,
+   |          ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
index 1b76669ccb0d..4f685c508c72 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr
@@ -16,23 +16,13 @@ LL |     where
 LL |         T: AsExpression,
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
 
-error[E0277]: the trait bound `&str: AsExpression` is not satisfied
-  --> $DIR/as_expression.rs:55:15
-   |
-LL |     SelectInt.check("bar");
-   |               ^^^^^ the trait `AsExpression` is not implemented for `&str`
-   |
-   = help: the trait `AsExpression` is not implemented for `&str`
-           but trait `AsExpression` is implemented for it
-   = help: for that trait implementation, expected `Text`, found `Integer`
-
 error[E0271]: type mismatch resolving `::SqlType == Text`
   --> $DIR/as_expression.rs:55:5
    |
 LL |     SelectInt.check("bar");
    |     ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0271, E0277.
 For more information about an error, try `rustc --explain E0271`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
index 583b3c4675a8..48c1ed2b02d7 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
@@ -53,7 +53,7 @@ impl Foo for T where T: Expression {}
 
 fn main() {
     SelectInt.check("bar");
-    //~^ ERROR the trait bound `&str: AsExpression` is not satisfied
-    //[next]~| the trait bound `&str: AsExpression<::SqlType>` is not satisfied
+    //[current]~^ ERROR the trait bound `&str: AsExpression` is not satisfied
+    //[next]~^^ the trait bound `&str: AsExpression<::SqlType>` is not satisfied
     //[next]~| type mismatch
 }
diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
index 1cfc2a6d9449..a95670ced867 100644
--- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
+++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
@@ -18,6 +18,11 @@ help: this trait has no implementations, consider adding one
    |
 LL | trait Foo {}
    | ^^^^^^^^^
+note: required by a bound in `A`
+  --> $DIR/alias-bounds-when-not-wf.rs:8:11
+   |
+LL | type A = T;
+   |           ^^^ required by this bound in `A`
 
 error[E0277]: the trait bound `usize: Foo` is not satisfied
   --> $DIR/alias-bounds-when-not-wf.rs:16:10
diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr
index 377dfc8b5291..425f2d59222e 100644
--- a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr
+++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr
@@ -1,8 +1,8 @@
-error[E0284]: type annotations needed: cannot normalize `X::{constant#0}`
+error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated`
   --> $DIR/const-region-infer-to-static-in-binder.rs:4:10
    |
 LL | struct X;
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}`
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated`
 
 error: using function pointers as const generic parameters is forbidden
   --> $DIR/const-region-infer-to-static-in-binder.rs:4:20
diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr
index b96bfab927d2..2d0c503bc958 100644
--- a/tests/ui/traits/next-solver/specialization-transmute.stderr
+++ b/tests/ui/traits/next-solver/specialization-transmute.stderr
@@ -10,11 +10,11 @@ LL | #![feature(specialization)]
 
 error: cannot normalize `::Id: '_`
 
-error[E0284]: type annotations needed: cannot normalize `::Id`
+error[E0282]: type annotations needed
   --> $DIR/specialization-transmute.rs:15:23
    |
 LL |     fn intu(&self) -> &Self::Id {
-   |                       ^^^^^^^^^ cannot normalize `::Id`
+   |                       ^^^^^^^^^ cannot infer type for reference `&::Id`
 
 error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to T`
   --> $DIR/specialization-transmute.rs:17:9
@@ -36,4 +36,5 @@ LL | fn transmute, U: Copy>(t: T) -> U {
 
 error: aborting due to 4 previous errors; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0284`.
+Some errors have detailed explanations: E0282, E0284.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr
index 1bcc0dbaf672..92ad83c33000 100644
--- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr
+++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr
@@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize`
    |
 LL |     fn func() -> [(); N];
    |                                ^^^^^^^ expected `usize`, found `u32`
+   |
+   = note: the length of array `[(); N]` must be type `usize`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/union/union-derive-eq.next.stderr b/tests/ui/union/union-derive-eq.next.stderr
index 3952b1f12840..151ceebe1ba6 100644
--- a/tests/ui/union/union-derive-eq.next.stderr
+++ b/tests/ui/union/union-derive-eq.next.stderr
@@ -7,6 +7,8 @@ LL | union U2 {
 LL |     a: PartialEqNotEq,
    |     ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
    |
+note: required by a bound in `AssertParamIsEq`
+  --> $SRC_DIR/core/src/cmp.rs:LL:COL
    = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]`
    |
diff --git a/tests/ui/wf/wf-normalization-sized.next.stderr b/tests/ui/wf/wf-normalization-sized.next.stderr
index 1e898fb7b78a..83b56bb6b19a 100644
--- a/tests/ui/wf/wf-normalization-sized.next.stderr
+++ b/tests/ui/wf/wf-normalization-sized.next.stderr
@@ -5,6 +5,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
    |           ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
+   = note: slice and array elements must have `Sized` type
 
 error[E0277]: the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time
   --> $DIR/wf-normalization-sized.rs:19:11
@@ -13,6 +14,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
    |           ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
+   = note: slice and array elements must have `Sized` type
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
@@ -22,6 +24,8 @@ LL | const _:  as WellUnformed>::RequestNormalize = ();
    |           ^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `str`
+note: required by an implicit `Sized` bound in `Vec`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
   --> $DIR/wf-normalization-sized.rs:22:11
@@ -30,6 +34,8 @@ LL | const _:  as WellUnformed>::RequestNormalize = ();
    |           ^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `str`
+note: required by an implicit `Sized` bound in `Vec`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: aborting due to 4 previous errors
diff --git a/tests/ui/wf/wf-trait-fn-arg.next.stderr b/tests/ui/wf/wf-trait-fn-arg.next.stderr
index c55dc5c8a121..d5dd36fad6dd 100644
--- a/tests/ui/wf/wf-trait-fn-arg.next.stderr
+++ b/tests/ui/wf/wf-trait-fn-arg.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
 LL |     fn bar(&self, x: &Bar);
    |                       ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
    |
+note: required by a bound in `Bar`
+  --> $DIR/wf-trait-fn-arg.rs:11:15
+   |
+LL | struct Bar {
+   |               ^^ required by this bound in `Bar`
 help: consider further restricting `Self`
    |
 LL |     fn bar(&self, x: &Bar) where Self: Eq;
diff --git a/tests/ui/wf/wf-trait-fn-ret.next.stderr b/tests/ui/wf/wf-trait-fn-ret.next.stderr
index b3dca17672d3..0ad786c2fd56 100644
--- a/tests/ui/wf/wf-trait-fn-ret.next.stderr
+++ b/tests/ui/wf/wf-trait-fn-ret.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
 LL |     fn bar(&self) -> &Bar;
    |                       ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
    |
+note: required by a bound in `Bar`
+  --> $DIR/wf-trait-fn-ret.rs:10:15
+   |
+LL | struct Bar {
+   |               ^^ required by this bound in `Bar`
 help: consider further restricting `Self`
    |
 LL |     fn bar(&self) -> &Bar where Self: Eq;
diff --git a/tests/ui/wf/wf-trait-fn-where-clause.next.stderr b/tests/ui/wf/wf-trait-fn-where-clause.next.stderr
index 8c8a5fa3e704..db5454d0f3c2 100644
--- a/tests/ui/wf/wf-trait-fn-where-clause.next.stderr
+++ b/tests/ui/wf/wf-trait-fn-where-clause.next.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
 LL |         Bar: Copy;
    |                    ^^^^ the trait `Eq` is not implemented for `Self`
    |
+note: required by a bound in `Bar`
+  --> $DIR/wf-trait-fn-where-clause.rs:10:15
+   |
+LL | struct Bar {
+   |               ^^ required by this bound in `Bar`
 help: consider further restricting `Self`
    |
 LL |         Bar: Copy, Self: Eq;

From 4f5116e236843492998acfa42381d14db360f2ab Mon Sep 17 00:00:00 2001
From: Tobias Decking 
Date: Sat, 1 Feb 2025 23:44:52 +0100
Subject: [PATCH 340/342] Use `widening_mul`

---
 library/core/src/fmt/num.rs | 21 +--------------------
 1 file changed, 1 insertion(+), 20 deletions(-)

diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index 683e45b35f70..fd548018dade 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -726,28 +726,9 @@ fn udiv_1e19(n: u128) -> (u128, u64) {
     let quot = if n < 1 << 83 {
         ((n >> 19) as u64 / (DIV >> 19)) as u128
     } else {
-        u128_mulhi(n, FACTOR) >> 62
+        n.widening_mul(FACTOR).1 >> 62
     };
 
     let rem = (n - quot * DIV as u128) as u64;
     (quot, rem)
 }
-
-/// Multiply unsigned 128 bit integers, return upper 128 bits of the result
-#[inline]
-fn u128_mulhi(x: u128, y: u128) -> u128 {
-    let x_lo = x as u64;
-    let x_hi = (x >> 64) as u64;
-    let y_lo = y as u64;
-    let y_hi = (y >> 64) as u64;
-
-    // handle possibility of overflow
-    let carry = (x_lo as u128 * y_lo as u128) >> 64;
-    let m = x_lo as u128 * y_hi as u128 + carry;
-    let high1 = m >> 64;
-
-    let m_lo = m as u64;
-    let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64;
-
-    x_hi as u128 * y_hi as u128 + high1 + high2
-}

From f09de673565b88dd0c9f416e171f5c099073270e Mon Sep 17 00:00:00 2001
From: The rustc-dev-guide Cronjob Bot 
Date: Sun, 2 Feb 2025 04:02:19 +0000
Subject: [PATCH 341/342] Preparing for merge from rustc

---
 src/doc/rustc-dev-guide/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index 183d26b29384..fa65931bdc52 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-66d6064f9eb888018775e08f84747ee6f39ba28e
+8239a37f9c0951a037cfc51763ea52a20e71e6bd

From 3827ff028971a6ec6ef18fd80f44c1cc05c898bb Mon Sep 17 00:00:00 2001
From: Yuki Okushi 
Date: Sun, 2 Feb 2025 17:30:30 +0900
Subject: [PATCH 342/342] Apply suggestions from code review

---
 src/doc/rustc-dev-guide/src/traits/implied-bounds.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md
index 05693dcd5a18..cdcb90d3e2ed 100644
--- a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md
+++ b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md
@@ -40,7 +40,7 @@ requirements of impls and functions as explicit predicates.
 ### using implicit implied bounds as assumptions
 
 These bounds are not added to the `ParamEnv` of the affected item itself. For lexical
-region resolution they are added using [`fn OutlivesEnvironment::new`].
+region resolution they are added using [`fn OutlivesEnvironment::from_normalized_bounds`].
 Similarly, during MIR borrowck we add them using
 [`fn UniversalRegionRelationsBuilder::add_implied_bounds`].
 
@@ -55,7 +55,7 @@ The assumed outlives constraints for implicit bounds are computed using the
 MIR borrowck adds the outlives constraints for both the normalized and unnormalized types,
 lexical region resolution [only uses the unnormalized types][notnorm].
 
-[`fn OutlivesEnvironment::new`]: TODO
+[`fn OutlivesEnvironment::from_normalized_bounds`]: https://github.com/rust-lang/rust/blob/8239a37f9c0951a037cfc51763ea52a20e71e6bd/compiler/rustc_infer/src/infer/outlives/env.rs#L50-L55
 [`fn UniversalRegionRelationsBuilder::add_implied_bounds`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs#L316
 [mir]: https://github.com/rust-lang/rust/blob/91cae1dcdcf1a31bd8a92e4a63793d65cfe289bb/compiler/rustc_borrowck/src/type_check/free_region_relations.rs#L258-L332
 [`fn assumed_wf_types`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_ty_utils/src/implied_bounds.rs#L21